Repository: pennersr/django-allauth Branch: main Commit: cf7f55d79e42 Files: 1853 Total size: 5.4 MB Directory structure: gitextract_12fbmuov/ ├── .dir-locals.el ├── .djlintrc ├── .editorconfig ├── .envrc ├── .gitea/ │ ├── ISSUE_TEMPLATE/ │ │ ├── discussion.md │ │ └── issue.md │ └── pull_request_template.md ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ └── issue.md │ ├── SECURITY.md │ ├── dependabot.yml │ ├── pull_request_template.md │ └── workflows/ │ └── ci.yml ├── .gitignore ├── .readthedocs.yaml ├── .woodpecker.yaml ├── AUTHORS ├── CONTRIBUTING.rst ├── ChangeLog.rst ├── LICENSE ├── Makefile ├── README.rst ├── allauth/ │ ├── __init__.py │ ├── account/ │ │ ├── __init__.py │ │ ├── adapter.py │ │ ├── admin.py │ │ ├── app_settings.py │ │ ├── apps.py │ │ ├── auth_backends.py │ │ ├── authentication.py │ │ ├── checks.py │ │ ├── decorators.py │ │ ├── fields.py │ │ ├── forms.py │ │ ├── internal/ │ │ │ ├── __init__.py │ │ │ ├── constants.py │ │ │ ├── decorators.py │ │ │ ├── emailkit.py │ │ │ ├── flows/ │ │ │ │ ├── __init__.py │ │ │ │ ├── code_verification.py │ │ │ │ ├── email_verification.py │ │ │ │ ├── email_verification_by_code.py │ │ │ │ ├── login.py │ │ │ │ ├── login_by_code.py │ │ │ │ ├── logout.py │ │ │ │ ├── manage_email.py │ │ │ │ ├── password_change.py │ │ │ │ ├── password_reset.py │ │ │ │ ├── password_reset_by_code.py │ │ │ │ ├── phone_verification.py │ │ │ │ ├── reauthentication.py │ │ │ │ └── signup.py │ │ │ ├── stagekit.py │ │ │ └── userkit.py │ │ ├── management/ │ │ │ ├── __init__.py │ │ │ └── commands/ │ │ │ ├── __init__.py │ │ │ └── account_unsetmultipleprimaryemails.py │ │ ├── managers.py │ │ ├── middleware.py │ │ ├── migrations/ │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_email_max_length.py │ │ │ ├── 0003_alter_emailaddress_create_unique_verified_email.py │ │ │ ├── 0004_alter_emailaddress_drop_unique_email.py │ │ │ ├── 0005_emailaddress_idx_upper_email.py │ │ │ ├── 0006_emailaddress_lower.py │ │ │ ├── 0007_emailaddress_idx_email.py │ │ │ ├── 0008_emailaddress_unique_primary_email_fixup.py │ │ │ ├── 0009_emailaddress_unique_primary_email.py │ │ │ └── __init__.py │ │ ├── mixins.py │ │ ├── models.py │ │ ├── reauthentication.py │ │ ├── signals.py │ │ ├── stages.py │ │ ├── static/ │ │ │ └── account/ │ │ │ └── js/ │ │ │ ├── account.js │ │ │ └── onload.js │ │ ├── templatetags/ │ │ │ ├── __init__.py │ │ │ └── account.py │ │ ├── urls.py │ │ ├── utils.py │ │ └── views.py │ ├── app_settings.py │ ├── core/ │ │ ├── __init__.py │ │ ├── context.py │ │ ├── exceptions.py │ │ ├── internal/ │ │ │ ├── __init__.py │ │ │ ├── adapter.py │ │ │ ├── cryptokit.py │ │ │ ├── httpkit.py │ │ │ ├── jwkkit.py │ │ │ ├── modelkit.py │ │ │ ├── ratelimit.py │ │ │ ├── sessionkit.py │ │ │ └── urlkit.py │ │ └── ratelimit.py │ ├── decorators.py │ ├── exceptions.py │ ├── headless/ │ │ ├── __init__.py │ │ ├── account/ │ │ │ ├── __init__.py │ │ │ ├── inputs.py │ │ │ ├── response.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ ├── adapter.py │ │ ├── app_settings.py │ │ ├── apps.py │ │ ├── base/ │ │ │ ├── __init__.py │ │ │ ├── response.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ ├── checks.py │ │ ├── constants.py │ │ ├── contrib/ │ │ │ ├── __init__.py │ │ │ ├── ninja/ │ │ │ │ ├── __init__.py │ │ │ │ └── security.py │ │ │ └── rest_framework/ │ │ │ ├── __init__.py │ │ │ └── authentication.py │ │ ├── internal/ │ │ │ ├── __init__.py │ │ │ ├── authkit.py │ │ │ ├── decorators.py │ │ │ ├── restkit/ │ │ │ │ ├── __init__.py │ │ │ │ ├── inputs.py │ │ │ │ ├── response.py │ │ │ │ └── views.py │ │ │ └── sessionkit.py │ │ ├── mfa/ │ │ │ ├── __init__.py │ │ │ ├── inputs.py │ │ │ ├── response.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ ├── socialaccount/ │ │ │ ├── __init__.py │ │ │ ├── forms.py │ │ │ ├── inputs.py │ │ │ ├── internal.py │ │ │ ├── response.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ ├── spec/ │ │ │ ├── __init__.py │ │ │ ├── doc/ │ │ │ │ ├── description.md │ │ │ │ └── openapi.yaml │ │ │ ├── internal/ │ │ │ │ ├── __init__.py │ │ │ │ ├── openapikit.py │ │ │ │ └── schema.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ ├── templates/ │ │ │ └── headless/ │ │ │ └── spec/ │ │ │ ├── redoc_cdn.html │ │ │ └── swagger_cdn.html │ │ ├── tokens/ │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── inputs.py │ │ │ ├── response.py │ │ │ ├── sessions.py │ │ │ ├── strategies/ │ │ │ │ ├── __init__.py │ │ │ │ ├── base.py │ │ │ │ ├── jwt/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── internal.py │ │ │ │ │ └── strategy.py │ │ │ │ └── sessions.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ ├── urls.py │ │ └── usersessions/ │ │ ├── __init__.py │ │ ├── inputs.py │ │ ├── response.py │ │ ├── urls.py │ │ └── views.py │ ├── idp/ │ │ ├── __init__.py │ │ ├── oidc/ │ │ │ ├── __init__.py │ │ │ ├── adapter.py │ │ │ ├── admin.py │ │ │ ├── app_settings.py │ │ │ ├── apps.py │ │ │ ├── contrib/ │ │ │ │ ├── __init__.py │ │ │ │ ├── ninja/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── security.py │ │ │ │ └── rest_framework/ │ │ │ │ ├── __init__.py │ │ │ │ ├── authentication.py │ │ │ │ └── permissions.py │ │ │ ├── forms.py │ │ │ ├── internal/ │ │ │ │ ├── __init__.py │ │ │ │ ├── clientkit.py │ │ │ │ ├── flows.py │ │ │ │ ├── oauthlib/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── authorization_codes.py │ │ │ │ │ ├── device_codes.py │ │ │ │ │ ├── request_validator.py │ │ │ │ │ ├── server.py │ │ │ │ │ └── utils.py │ │ │ │ ├── scope.py │ │ │ │ └── tokens.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_client_default_scopes.py │ │ │ │ ├── 0003_client_allow_uri_wildcards.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ └── urls.py │ ├── locale/ │ │ ├── ar/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── az/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── bg/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── ca/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── cs/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── da/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── de/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── el/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── en/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── es/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── et/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── eu/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── fa/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── fi/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── fr/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── he/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── hr/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── ht/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── hu/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── id/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── it/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── ja/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── ka/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── ko/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── ky/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── lt/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── lv/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── mn/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── nb/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── nl/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── pl/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── pt_BR/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── pt_PT/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── ro/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── ru/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── sk/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── sl/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── sr/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── sr_Latn/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── sv/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── th/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── tr/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── uk/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── uz/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ ├── zh_Hans/ │ │ │ └── LC_MESSAGES/ │ │ │ └── django.po │ │ └── zh_Hant/ │ │ └── LC_MESSAGES/ │ │ └── django.po │ ├── mfa/ │ │ ├── __init__.py │ │ ├── adapter.py │ │ ├── admin.py │ │ ├── app_settings.py │ │ ├── apps.py │ │ ├── base/ │ │ │ ├── __init__.py │ │ │ ├── forms.py │ │ │ ├── internal/ │ │ │ │ ├── __init__.py │ │ │ │ └── flows.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ ├── checks.py │ │ ├── internal/ │ │ │ ├── __init__.py │ │ │ ├── constants.py │ │ │ └── flows/ │ │ │ ├── __init__.py │ │ │ ├── add.py │ │ │ └── trust.py │ │ ├── migrations/ │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_authenticator_timestamps.py │ │ │ ├── 0003_authenticator_type_uniq.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── recovery_codes/ │ │ │ ├── __init__.py │ │ │ ├── forms.py │ │ │ ├── internal/ │ │ │ │ ├── __init__.py │ │ │ │ ├── auth.py │ │ │ │ └── flows.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ ├── signals.py │ │ ├── stages.py │ │ ├── static/ │ │ │ └── mfa/ │ │ │ └── js/ │ │ │ ├── webauthn-json.js │ │ │ └── webauthn.js │ │ ├── totp/ │ │ │ ├── __init__.py │ │ │ ├── forms.py │ │ │ ├── internal/ │ │ │ │ ├── __init__.py │ │ │ │ ├── auth.py │ │ │ │ └── flows.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ ├── urls.py │ │ ├── utils.py │ │ └── webauthn/ │ │ ├── __init__.py │ │ ├── forms.py │ │ ├── internal/ │ │ │ ├── __init__.py │ │ │ ├── auth.py │ │ │ └── flows.py │ │ ├── stages.py │ │ ├── urls.py │ │ └── views.py │ ├── models.py │ ├── ratelimit.py │ ├── socialaccount/ │ │ ├── __init__.py │ │ ├── adapter.py │ │ ├── admin.py │ │ ├── app_settings.py │ │ ├── apps.py │ │ ├── checks.py │ │ ├── forms.py │ │ ├── helpers.py │ │ ├── internal/ │ │ │ ├── __init__.py │ │ │ ├── flows/ │ │ │ │ ├── __init__.py │ │ │ │ ├── connect.py │ │ │ │ ├── email_authentication.py │ │ │ │ ├── login.py │ │ │ │ └── signup.py │ │ │ ├── jwtkit.py │ │ │ └── statekit.py │ │ ├── migrations/ │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_token_max_lengths.py │ │ │ ├── 0003_extra_data_default_dict.py │ │ │ ├── 0004_app_provider_id_settings.py │ │ │ ├── 0005_socialtoken_nullable_app.py │ │ │ ├── 0006_alter_socialaccount_extra_data.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── providers/ │ │ │ ├── __init__.py │ │ │ ├── agave/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── amazon/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── amazon_cognito/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ ├── utils.py │ │ │ │ └── views.py │ │ │ ├── angellist/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── apple/ │ │ │ │ ├── __init__.py │ │ │ │ ├── apple_session.py │ │ │ │ ├── client.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── asana/ │ │ │ │ ├── __init__.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── atlassian/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── auth0/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── authentiq/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── baidu/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── base/ │ │ │ │ ├── __init__.py │ │ │ │ ├── constants.py │ │ │ │ ├── provider.py │ │ │ │ ├── utils.py │ │ │ │ └── views.py │ │ │ ├── basecamp/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── battlenet/ │ │ │ │ ├── __init__.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ ├── validators.py │ │ │ │ └── views.py │ │ │ ├── bitbucket_oauth2/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── bitly/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── box/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── cilogon/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── clever/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── coinbase/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── dataporten/ │ │ │ │ ├── __init__.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── daum/ │ │ │ │ ├── __init__.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── digitalocean/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── dingtalk/ │ │ │ │ ├── __init__.py │ │ │ │ ├── client.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── discogs/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── discord/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── disqus/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── douban/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── doximity/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── draugiem/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── drip/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── dropbox/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── dummy/ │ │ │ │ ├── __init__.py │ │ │ │ ├── forms.py │ │ │ │ ├── provider.py │ │ │ │ ├── templates/ │ │ │ │ │ └── dummy/ │ │ │ │ │ └── authenticate_form.html │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── dwolla/ │ │ │ │ ├── __init__.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── edmodo/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── edx/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── eventbrite/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── eveonline/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── evernote/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── exist/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── facebook/ │ │ │ │ ├── __init__.py │ │ │ │ ├── constants.py │ │ │ │ ├── data/ │ │ │ │ │ └── FacebookLocales.xml │ │ │ │ ├── flows.py │ │ │ │ ├── forms.py │ │ │ │ ├── locale.py │ │ │ │ ├── provider.py │ │ │ │ ├── static/ │ │ │ │ │ └── facebook/ │ │ │ │ │ └── js/ │ │ │ │ │ └── fbconnect.js │ │ │ │ ├── templates/ │ │ │ │ │ └── facebook/ │ │ │ │ │ └── fbconnect.html │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── feedly/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── feishu/ │ │ │ │ ├── __init__.py │ │ │ │ ├── client.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── figma/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── fivehundredpx/ │ │ │ │ ├── __init__.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── flickr/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── foursquare/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── frontier/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── fxa/ │ │ │ │ ├── __init__.py │ │ │ │ ├── constants.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── gitea/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── github/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── gitlab/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── globus/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── google/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── gumroad/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── hubic/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── hubspot/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── instagram/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── jupyterhub/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── kakao/ │ │ │ │ ├── __init__.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── lemonldap/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── lichess/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── line/ │ │ │ │ ├── __init__.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── linkedin_oauth2/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── mailchimp/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── mailcow/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── mailru/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── mediawiki/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── meetup/ │ │ │ │ ├── __init__.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── microsoft/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── miro/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── naver/ │ │ │ │ ├── __init__.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── netiq/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── nextcloud/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── notion/ │ │ │ │ ├── __init__.py │ │ │ │ ├── client.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── oauth/ │ │ │ │ ├── __init__.py │ │ │ │ ├── client.py │ │ │ │ ├── oauth1_auth.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── oauth2/ │ │ │ │ ├── __init__.py │ │ │ │ ├── client.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ ├── utils.py │ │ │ │ └── views.py │ │ │ ├── odnoklassniki/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── okta/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── openid/ │ │ │ │ ├── __init__.py │ │ │ │ ├── admin.py │ │ │ │ ├── forms.py │ │ │ │ ├── migrations/ │ │ │ │ │ ├── 0001_initial.py │ │ │ │ │ └── __init__.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ ├── utils.py │ │ │ │ └── views.py │ │ │ ├── openid_connect/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── openstreetmap/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── orcid/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── patreon/ │ │ │ │ ├── __init__.py │ │ │ │ ├── constants.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── paypal/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── pinterest/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── pocket/ │ │ │ │ ├── __init__.py │ │ │ │ ├── client.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── questrade/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── quickbooks/ │ │ │ │ ├── __init__.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── reddit/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── robinhood/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── salesforce/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── saml/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ ├── utils.py │ │ │ │ └── views.py │ │ │ ├── sharefile/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── shopify/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── slack/ │ │ │ │ ├── __init__.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── snapchat/ │ │ │ │ ├── __init__.py │ │ │ │ ├── constants.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── soundcloud/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── spotify/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── stackexchange/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── steam/ │ │ │ │ ├── __init__.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── stocktwits/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── strava/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── stripe/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── telegram/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── static/ │ │ │ │ │ └── telegram/ │ │ │ │ │ └── js/ │ │ │ │ │ └── telegram.js │ │ │ │ ├── templates/ │ │ │ │ │ └── telegram/ │ │ │ │ │ └── callback.html │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── tiktok/ │ │ │ │ ├── __init__.py │ │ │ │ ├── client.py │ │ │ │ ├── provider.py │ │ │ │ ├── scope.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── trainingpeaks/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── trello/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── tumblr/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── tumblr_oauth2/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── twentythreeandme/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── twitch/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── twitter/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── twitter_oauth2/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── untappd/ │ │ │ │ ├── __init__.py │ │ │ │ ├── client.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── vimeo/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── vimeo_oauth2/ │ │ │ │ ├── __init__.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── vk/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── wahoo/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── weibo/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── weixin/ │ │ │ │ ├── __init__.py │ │ │ │ ├── client.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── windowslive/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── xing/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── yahoo/ │ │ │ │ ├── __init__.py │ │ │ │ ├── models.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── yandex/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── ynab/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ ├── zoho/ │ │ │ │ ├── __init__.py │ │ │ │ ├── provider.py │ │ │ │ ├── urls.py │ │ │ │ └── views.py │ │ │ └── zoom/ │ │ │ ├── __init__.py │ │ │ ├── provider.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ ├── sessions.py │ │ ├── signals.py │ │ ├── templatetags/ │ │ │ ├── __init__.py │ │ │ └── socialaccount.py │ │ ├── urls.py │ │ └── views.py │ ├── templates/ │ │ ├── account/ │ │ │ ├── account_inactive.html │ │ │ ├── base_confirm_code.html │ │ │ ├── base_entrance.html │ │ │ ├── base_manage.html │ │ │ ├── base_manage_email.html │ │ │ ├── base_manage_password.html │ │ │ ├── base_manage_phone.html │ │ │ ├── base_reauthenticate.html │ │ │ ├── confirm_email_verification_code.html │ │ │ ├── confirm_login_code.html │ │ │ ├── confirm_password_reset_code.html │ │ │ ├── confirm_phone_verification_code.html │ │ │ ├── email/ │ │ │ │ ├── account_already_exists_message.txt │ │ │ │ ├── account_already_exists_subject.txt │ │ │ │ ├── base_message.txt │ │ │ │ ├── base_notification.txt │ │ │ │ ├── email_changed_message.txt │ │ │ │ ├── email_changed_subject.txt │ │ │ │ ├── email_confirm_message.txt │ │ │ │ ├── email_confirm_subject.txt │ │ │ │ ├── email_confirmation_message.txt │ │ │ │ ├── email_confirmation_signup_message.txt │ │ │ │ ├── email_confirmation_signup_subject.txt │ │ │ │ ├── email_confirmation_subject.txt │ │ │ │ ├── email_deleted_message.txt │ │ │ │ ├── email_deleted_subject.txt │ │ │ │ ├── login_code_message.txt │ │ │ │ ├── login_code_subject.txt │ │ │ │ ├── password_changed_message.txt │ │ │ │ ├── password_changed_subject.txt │ │ │ │ ├── password_reset_code_message.txt │ │ │ │ ├── password_reset_code_subject.txt │ │ │ │ ├── password_reset_key_message.txt │ │ │ │ ├── password_reset_key_subject.txt │ │ │ │ ├── password_reset_message.txt │ │ │ │ ├── password_reset_subject.txt │ │ │ │ ├── password_set_message.txt │ │ │ │ ├── password_set_subject.txt │ │ │ │ ├── unknown_account_message.txt │ │ │ │ └── unknown_account_subject.txt │ │ │ ├── email.html │ │ │ ├── email_change.html │ │ │ ├── email_confirm.html │ │ │ ├── login.html │ │ │ ├── logout.html │ │ │ ├── messages/ │ │ │ │ ├── cannot_delete_primary_email.txt │ │ │ │ ├── email_confirmation_failed.txt │ │ │ │ ├── email_confirmation_sent.txt │ │ │ │ ├── email_confirmed.txt │ │ │ │ ├── email_deleted.txt │ │ │ │ ├── logged_in.txt │ │ │ │ ├── logged_out.txt │ │ │ │ ├── login_code_sent.txt │ │ │ │ ├── password_changed.txt │ │ │ │ ├── password_set.txt │ │ │ │ ├── phone_verification_sent.txt │ │ │ │ ├── phone_verified.txt │ │ │ │ ├── primary_email_set.txt │ │ │ │ └── unverified_primary_email.txt │ │ │ ├── password_change.html │ │ │ ├── password_reset.html │ │ │ ├── password_reset_done.html │ │ │ ├── password_reset_from_key.html │ │ │ ├── password_reset_from_key_done.html │ │ │ ├── password_set.html │ │ │ ├── phone_change.html │ │ │ ├── reauthenticate.html │ │ │ ├── request_login_code.html │ │ │ ├── signup.html │ │ │ ├── signup_by_passkey.html │ │ │ ├── signup_closed.html │ │ │ ├── snippets/ │ │ │ │ ├── already_logged_in.html │ │ │ │ └── warn_no_email.html │ │ │ ├── verification_sent.html │ │ │ └── verified_email_required.html │ │ ├── allauth/ │ │ │ ├── elements/ │ │ │ │ ├── alert.html │ │ │ │ ├── badge.html │ │ │ │ ├── button.html │ │ │ │ ├── button_group.html │ │ │ │ ├── details.html │ │ │ │ ├── field.html │ │ │ │ ├── fields.html │ │ │ │ ├── form.html │ │ │ │ ├── h1.html │ │ │ │ ├── h2.html │ │ │ │ ├── hr.html │ │ │ │ ├── img.html │ │ │ │ ├── p.html │ │ │ │ ├── panel.html │ │ │ │ ├── provider.html │ │ │ │ ├── provider_list.html │ │ │ │ ├── table.html │ │ │ │ ├── tbody.html │ │ │ │ ├── td.html │ │ │ │ ├── th.html │ │ │ │ ├── thead.html │ │ │ │ └── tr.html │ │ │ └── layouts/ │ │ │ ├── base.html │ │ │ ├── entrance.html │ │ │ └── manage.html │ │ ├── idp/ │ │ │ └── oidc/ │ │ │ ├── authorization_form.html │ │ │ ├── base.html │ │ │ ├── device_authorization_code_form.html │ │ │ ├── device_authorization_confirm_form.html │ │ │ ├── device_authorization_confirmed.html │ │ │ ├── device_authorization_denied.html │ │ │ ├── error.html │ │ │ └── logout.html │ │ ├── mfa/ │ │ │ ├── authenticate.html │ │ │ ├── base_entrance.html │ │ │ ├── base_manage.html │ │ │ ├── email/ │ │ │ │ ├── recovery_codes_generated_message.txt │ │ │ │ ├── recovery_codes_generated_subject.txt │ │ │ │ ├── totp_activated_message.txt │ │ │ │ ├── totp_activated_subject.txt │ │ │ │ ├── totp_deactivated_message.txt │ │ │ │ ├── totp_deactivated_subject.txt │ │ │ │ ├── webauthn_added_message.txt │ │ │ │ ├── webauthn_added_subject.txt │ │ │ │ ├── webauthn_removed_message.txt │ │ │ │ └── webauthn_removed_subject.txt │ │ │ ├── index.html │ │ │ ├── messages/ │ │ │ │ ├── recovery_codes_generated.txt │ │ │ │ ├── totp_activated.txt │ │ │ │ ├── totp_deactivated.txt │ │ │ │ ├── webauthn_added.txt │ │ │ │ └── webauthn_removed.txt │ │ │ ├── reauthenticate.html │ │ │ ├── recovery_codes/ │ │ │ │ ├── base.html │ │ │ │ ├── download.txt │ │ │ │ ├── generate.html │ │ │ │ └── index.html │ │ │ ├── totp/ │ │ │ │ ├── activate_form.html │ │ │ │ ├── base.html │ │ │ │ └── deactivate_form.html │ │ │ ├── trust.html │ │ │ └── webauthn/ │ │ │ ├── add_form.html │ │ │ ├── authenticator_confirm_delete.html │ │ │ ├── authenticator_list.html │ │ │ ├── base.html │ │ │ ├── edit_form.html │ │ │ ├── reauthenticate.html │ │ │ ├── signup_form.html │ │ │ └── snippets/ │ │ │ ├── login_script.html │ │ │ └── scripts.html │ │ ├── openid/ │ │ │ ├── base.html │ │ │ └── login.html │ │ ├── socialaccount/ │ │ │ ├── authentication_error.html │ │ │ ├── base_entrance.html │ │ │ ├── base_manage.html │ │ │ ├── connections.html │ │ │ ├── email/ │ │ │ │ ├── account_connected_message.txt │ │ │ │ ├── account_connected_subject.txt │ │ │ │ ├── account_disconnected_message.txt │ │ │ │ └── account_disconnected_subject.txt │ │ │ ├── login.html │ │ │ ├── login_cancelled.html │ │ │ ├── login_redirect.html │ │ │ ├── messages/ │ │ │ │ ├── account_connected.txt │ │ │ │ ├── account_connected_other.txt │ │ │ │ ├── account_connected_updated.txt │ │ │ │ └── account_disconnected.txt │ │ │ ├── signup.html │ │ │ └── snippets/ │ │ │ ├── login.html │ │ │ ├── login_extra.html │ │ │ └── provider_list.html │ │ └── usersessions/ │ │ ├── base_manage.html │ │ ├── messages/ │ │ │ └── sessions_logged_out.txt │ │ └── usersession_list.html │ ├── templatetags/ │ │ ├── __init__.py │ │ └── allauth.py │ ├── urls.py │ ├── usersessions/ │ │ ├── __init__.py │ │ ├── adapter.py │ │ ├── admin.py │ │ ├── app_settings.py │ │ ├── apps.py │ │ ├── forms.py │ │ ├── internal/ │ │ │ ├── __init__.py │ │ │ └── flows/ │ │ │ ├── __init__.py │ │ │ └── sessions.py │ │ ├── middleware.py │ │ ├── migrations/ │ │ │ ├── 0001_initial.py │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── signals.py │ │ ├── urls.py │ │ └── views.py │ └── utils.py ├── devenv.nix ├── devenv.yaml ├── docs/ │ ├── Makefile │ ├── account/ │ │ ├── adapter.rst │ │ ├── advanced.rst │ │ ├── configuration.rst │ │ ├── decorators.rst │ │ ├── email.rst │ │ ├── forms.rst │ │ ├── index.rst │ │ ├── introduction.rst │ │ ├── phone.rst │ │ ├── rate_limits.rst │ │ ├── signals.rst │ │ ├── templates.rst │ │ └── views.rst │ ├── common/ │ │ ├── admin.rst │ │ ├── configuration.rst │ │ ├── email.rst │ │ ├── index.rst │ │ ├── messages.rst │ │ ├── rate_limits.rst │ │ └── templates.rst │ ├── conf.py │ ├── faq.rst │ ├── headless/ │ │ ├── adapter.rst │ │ ├── api.rst │ │ ├── configuration.rst │ │ ├── cors.rst │ │ ├── faq.rst │ │ ├── index.rst │ │ ├── installation.rst │ │ ├── introduction.rst │ │ ├── openapi-specification/ │ │ │ └── index.html │ │ └── token-strategies/ │ │ ├── abstract-strategy.rst │ │ ├── index.rst │ │ ├── introduction.rst │ │ ├── jwt-tokens.rst │ │ └── session-tokens.rst │ ├── idp/ │ │ ├── index.rst │ │ └── openid-connect/ │ │ ├── adapter.rst │ │ ├── clients.rst │ │ ├── configuration.rst │ │ ├── index.rst │ │ ├── installation.rst │ │ ├── integrations.rst │ │ ├── introduction.rst │ │ └── views.rst │ ├── index.rst │ ├── installation/ │ │ ├── examples.rst │ │ ├── index.rst │ │ ├── quickstart.rst │ │ └── requirements.rst │ ├── introduction/ │ │ └── index.rst │ ├── mfa/ │ │ ├── adapter.rst │ │ ├── configuration.rst │ │ ├── django-allauth-2fa.rst │ │ ├── forms.rst │ │ ├── index.rst │ │ ├── introduction.rst │ │ └── webauthn.rst │ ├── project/ │ │ ├── contributing.rst │ │ ├── funding.rst │ │ ├── index.rst │ │ └── support.rst │ ├── release-notes/ │ │ ├── 2012.rst │ │ ├── 2013.rst │ │ ├── 2014.rst │ │ ├── 2015.rst │ │ ├── 2016.rst │ │ ├── 2017.rst │ │ ├── 2018.rst │ │ ├── 2019.rst │ │ ├── 2020.rst │ │ ├── 2021.rst │ │ ├── 2022.rst │ │ ├── 2023.rst │ │ ├── 2024.rst │ │ ├── history.rst │ │ ├── index.rst │ │ └── recent.rst │ ├── requirements.txt │ ├── settings.py │ ├── socialaccount/ │ │ ├── adapter.rst │ │ ├── advanced.rst │ │ ├── configuration.rst │ │ ├── forms.rst │ │ ├── index.rst │ │ ├── introduction.rst │ │ ├── provider_configuration.rst │ │ ├── providers/ │ │ │ ├── 23andme.rst │ │ │ ├── 500px.rst │ │ │ ├── agave.rst │ │ │ ├── amazon.rst │ │ │ ├── amazon_cognito.rst │ │ │ ├── angellist.rst │ │ │ ├── apple.rst │ │ │ ├── atlassian.rst │ │ │ ├── auth0.rst │ │ │ ├── authelia.rst │ │ │ ├── authentiq.rst │ │ │ ├── baidu.rst │ │ │ ├── basecamp.rst │ │ │ ├── battlenet.rst │ │ │ ├── bitbucket.rst │ │ │ ├── box.rst │ │ │ ├── cern.rst │ │ │ ├── cilogon.rst │ │ │ ├── clever.rst │ │ │ ├── dataporten.rst │ │ │ ├── daum.rst │ │ │ ├── digitalocean.rst │ │ │ ├── dingtalk.rst │ │ │ ├── discogs.rst │ │ │ ├── discord.rst │ │ │ ├── doximity.rst │ │ │ ├── draugiem.rst │ │ │ ├── drip.rst │ │ │ ├── dropbox.rst │ │ │ ├── dwolla.rst │ │ │ ├── edmodo.rst │ │ │ ├── edx.rst │ │ │ ├── eventbrite.rst │ │ │ ├── eveonline.rst │ │ │ ├── evernote.rst │ │ │ ├── exist.rst │ │ │ ├── facebook.rst │ │ │ ├── feishu.rst │ │ │ ├── figma.rst │ │ │ ├── flickr.rst │ │ │ ├── frontier.rst │ │ │ ├── fxa.rst │ │ │ ├── gitea.rst │ │ │ ├── github.rst │ │ │ ├── gitlab.rst │ │ │ ├── globus.rst │ │ │ ├── google.rst │ │ │ ├── gumroad.rst │ │ │ ├── hubspot.rst │ │ │ ├── index.rst │ │ │ ├── instagram.rst │ │ │ ├── jupyterhub.rst │ │ │ ├── kakao.rst │ │ │ ├── keycloak.rst │ │ │ ├── lemonldap.rst │ │ │ ├── lichess.rst │ │ │ ├── line.rst │ │ │ ├── linkedin.rst │ │ │ ├── mailchimp.rst │ │ │ ├── mailcow.rst │ │ │ ├── mediawiki.rst │ │ │ ├── microsoft.rst │ │ │ ├── miro.rst │ │ │ ├── naver.rst │ │ │ ├── netiq.rst │ │ │ ├── nextcloud.rst │ │ │ ├── notion.rst │ │ │ ├── oauth2.rst │ │ │ ├── odnoklassniki.rst │ │ │ ├── okta.rst │ │ │ ├── openid.rst │ │ │ ├── openid_connect.rst │ │ │ ├── openstreetmap.rst │ │ │ ├── orcid.rst │ │ │ ├── patreon.rst │ │ │ ├── paypal.rst │ │ │ ├── pinterest.rst │ │ │ ├── pocket.rst │ │ │ ├── questrade.rst │ │ │ ├── quickbooks.rst │ │ │ ├── reddit.rst │ │ │ ├── salesforce.rst │ │ │ ├── saml.rst │ │ │ ├── sharefile.rst │ │ │ ├── shopify.rst │ │ │ ├── slack.rst │ │ │ ├── snapchat.rst │ │ │ ├── soundcloud.rst │ │ │ ├── stackexchange.rst │ │ │ ├── steam.rst │ │ │ ├── stocktwits.rst │ │ │ ├── strava.rst │ │ │ ├── stripe.rst │ │ │ ├── telegram.rst │ │ │ ├── tiktok.rst │ │ │ ├── trainingpeaks.rst │ │ │ ├── trello.rst │ │ │ ├── tumblr_oauth2.rst │ │ │ ├── twitch.rst │ │ │ ├── twitter.rst │ │ │ ├── twitter_oauth2.rst │ │ │ ├── untappd.rst │ │ │ ├── vimeo.rst │ │ │ ├── vimeo_oauth2.rst │ │ │ ├── vk.rst │ │ │ ├── wahoo.rst │ │ │ ├── weibo.rst │ │ │ ├── weixin.rst │ │ │ ├── windowslive.rst │ │ │ ├── xing.rst │ │ │ ├── yahoo.rst │ │ │ ├── yandex.rst │ │ │ ├── ynab.rst │ │ │ ├── zoho.rst │ │ │ └── zoom.rst │ │ ├── signals.rst │ │ ├── templates.rst │ │ └── views.rst │ └── usersessions/ │ ├── adapter.rst │ ├── configuration.rst │ ├── index.rst │ ├── installation.rst │ ├── introduction.rst │ └── signals.rst ├── examples/ │ ├── react-spa/ │ │ ├── Makefile │ │ ├── README.org │ │ ├── backend/ │ │ │ ├── Dockerfile │ │ │ ├── backend/ │ │ │ │ ├── __init__.py │ │ │ │ ├── asgi.py │ │ │ │ ├── drf_demo/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── serializers.py │ │ │ │ │ ├── urls.py │ │ │ │ │ └── views.py │ │ │ │ ├── ninja_demo/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── urls.py │ │ │ │ │ └── views.py │ │ │ │ ├── settings.py │ │ │ │ ├── urls.py │ │ │ │ └── wsgi.py │ │ │ ├── manage.py │ │ │ └── requirements.txt │ │ ├── docker-compose.dev.yml │ │ ├── docker-compose.yml │ │ ├── e2e.spec.js │ │ ├── frontend/ │ │ │ ├── Dockerfile │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ ├── css/ │ │ │ │ │ └── style.css │ │ │ │ └── index.html │ │ │ └── src/ │ │ │ ├── App.js │ │ │ ├── Calculator.js │ │ │ ├── Home.js │ │ │ ├── NavBar.js │ │ │ ├── Root.js │ │ │ ├── Router.js │ │ │ ├── account/ │ │ │ │ ├── AuthenticateFlow.js │ │ │ │ ├── ChangeEmail.js │ │ │ │ ├── ChangePassword.js │ │ │ │ ├── ConfirmLoginCode.js │ │ │ │ ├── ConfirmPasswordResetCode.js │ │ │ │ ├── Login.js │ │ │ │ ├── Logout.js │ │ │ │ ├── Reauthenticate.js │ │ │ │ ├── ReauthenticateFlow.js │ │ │ │ ├── RequestLoginCode.js │ │ │ │ ├── RequestPasswordReset.js │ │ │ │ ├── ResetPassword.js │ │ │ │ ├── Signup.js │ │ │ │ ├── VerificationEmailSent.js │ │ │ │ ├── VerifyEmail.js │ │ │ │ └── VerifyEmailByCode.js │ │ │ ├── auth/ │ │ │ │ ├── AuthContext.js │ │ │ │ ├── hooks.js │ │ │ │ ├── index.js │ │ │ │ └── routing.js │ │ │ ├── components/ │ │ │ │ ├── Button.js │ │ │ │ └── FormErrors.js │ │ │ ├── index.js │ │ │ ├── init.js │ │ │ ├── lib/ │ │ │ │ ├── allauth.js │ │ │ │ └── django.js │ │ │ ├── mfa/ │ │ │ │ ├── ActivateTOTP.js │ │ │ │ ├── AddWebAuthn.js │ │ │ │ ├── AuthenticateCode.js │ │ │ │ ├── AuthenticateFlow.js │ │ │ │ ├── AuthenticateRecoveryCodes.js │ │ │ │ ├── AuthenticateTOTP.js │ │ │ │ ├── AuthenticateWebAuthn.js │ │ │ │ ├── CreateSignupPasskey.js │ │ │ │ ├── DeactivateTOTP.js │ │ │ │ ├── GenerateRecoveryCodes.js │ │ │ │ ├── ListWebAuthn.js │ │ │ │ ├── MFAOverview.js │ │ │ │ ├── ReauthenticateCode.js │ │ │ │ ├── ReauthenticateRecoveryCodes.js │ │ │ │ ├── ReauthenticateTOTP.js │ │ │ │ ├── ReauthenticateWebAuthn.js │ │ │ │ ├── RecoveryCodes.js │ │ │ │ ├── SignupByPasskey.js │ │ │ │ ├── Trust.js │ │ │ │ └── WebAuthnLoginButton.js │ │ │ ├── socialaccount/ │ │ │ │ ├── GoogleOneTap.js │ │ │ │ ├── ManageProviders.js │ │ │ │ ├── ProviderCallback.js │ │ │ │ ├── ProviderList.js │ │ │ │ └── ProviderSignup.js │ │ │ └── usersessions/ │ │ │ └── Sessions.js │ │ └── traefik.toml │ └── regular-django/ │ ├── .dockerignore │ ├── Dockerfile │ ├── Makefile │ ├── README.org │ ├── db/ │ │ └── .gitignore │ ├── docker-compose.yml │ ├── example/ │ │ ├── __init__.py │ │ ├── asgi.py │ │ ├── settings.py │ │ ├── templates/ │ │ │ ├── 429.html │ │ │ ├── account/ │ │ │ │ ├── base_manage_email.html │ │ │ │ ├── base_manage_password.html │ │ │ │ └── base_manage_phone.html │ │ │ ├── allauth/ │ │ │ │ ├── elements/ │ │ │ │ │ ├── alert.html │ │ │ │ │ ├── badge.html │ │ │ │ │ ├── button.html │ │ │ │ │ ├── button__entrance.html │ │ │ │ │ ├── button_group.html │ │ │ │ │ ├── field.html │ │ │ │ │ ├── fields.html │ │ │ │ │ ├── form.html │ │ │ │ │ ├── form__entrance.html │ │ │ │ │ ├── h1.html │ │ │ │ │ ├── h1__entrance.html │ │ │ │ │ ├── h2__entrance.html │ │ │ │ │ ├── img.html │ │ │ │ │ ├── panel.html │ │ │ │ │ ├── provider.html │ │ │ │ │ ├── provider_list.html │ │ │ │ │ └── table.html │ │ │ │ └── layouts/ │ │ │ │ ├── base.html │ │ │ │ ├── entrance.html │ │ │ │ └── manage.html │ │ │ ├── index.html │ │ │ ├── mfa/ │ │ │ │ └── base_manage.html │ │ │ ├── profile.html │ │ │ ├── socialaccount/ │ │ │ │ └── base_manage.html │ │ │ └── usersessions/ │ │ │ └── base_manage.html │ │ ├── urls.py │ │ ├── users/ │ │ │ ├── __init__.py │ │ │ ├── allauth.py │ │ │ ├── apps.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ └── __init__.py │ │ │ └── models.py │ │ └── wsgi.py │ ├── manage.py │ └── requirements.txt ├── manage.py ├── mise.toml ├── noxfile.py ├── package.json ├── pyproject.toml ├── pytest.ini ├── requirements-dev.txt ├── setup.cfg └── tests/ ├── __init__.py ├── apps/ │ ├── __init__.py │ ├── account/ │ │ ├── __init__.py │ │ ├── internal/ │ │ │ ├── __init__.py │ │ │ └── test_emailkit.py │ │ ├── test_adapter.py │ │ ├── test_ajax.py │ │ ├── test_auth_backends.py │ │ ├── test_change_email.py │ │ ├── test_change_password.py │ │ ├── test_commands.py │ │ ├── test_decorators.py │ │ ├── test_email_verification.py │ │ ├── test_email_verification_by_code.py │ │ ├── test_login.py │ │ ├── test_login_by_code.py │ │ ├── test_logout.py │ │ ├── test_middleware.py │ │ ├── test_models.py │ │ ├── test_phone.py │ │ ├── test_ratelimit.py │ │ ├── test_reauthentication.py │ │ ├── test_reset_password.py │ │ ├── test_reset_password_by_code.py │ │ ├── test_security.py │ │ ├── test_signup.py │ │ └── test_utils.py │ ├── core/ │ │ ├── __init__.py │ │ └── internal/ │ │ ├── __init__.py │ │ ├── test_cryptokit.py │ │ ├── test_httpkit.py │ │ ├── test_modelkit.py │ │ └── test_ratelimit.py │ ├── headless/ │ │ ├── __init__.py │ │ ├── account/ │ │ │ ├── __init__.py │ │ │ ├── test_change_email.py │ │ │ ├── test_change_password.py │ │ │ ├── test_email_verification.py │ │ │ ├── test_email_verification_by_code.py │ │ │ ├── test_login.py │ │ │ ├── test_login_by_code.py │ │ │ ├── test_phone.py │ │ │ ├── test_reauthentication.py │ │ │ ├── test_reset_password.py │ │ │ ├── test_reset_password_by_code.py │ │ │ ├── test_session.py │ │ │ └── test_signup.py │ │ ├── base/ │ │ │ ├── __init__.py │ │ │ └── test_views.py │ │ ├── conftest.py │ │ ├── contrib/ │ │ │ ├── __init__.py │ │ │ ├── ninja/ │ │ │ │ ├── __init__.py │ │ │ │ └── test_security.py │ │ │ └── rest_framework/ │ │ │ ├── __init__.py │ │ │ └── test_authentication.py │ │ ├── internal/ │ │ │ ├── __init__.py │ │ │ └── test_authkit.py │ │ ├── mfa/ │ │ │ ├── __init__.py │ │ │ ├── test_recovery_codes.py │ │ │ ├── test_totp.py │ │ │ ├── test_trust.py │ │ │ ├── test_views.py │ │ │ └── test_webauthn.py │ │ ├── socialaccount/ │ │ │ ├── __init__.py │ │ │ ├── test_inputs.py │ │ │ └── test_views.py │ │ ├── spec/ │ │ │ ├── __init__.py │ │ │ ├── internal/ │ │ │ │ ├── __init__.py │ │ │ │ └── test_openapikit.py │ │ │ └── test_views.py │ │ ├── tokens/ │ │ │ ├── test_jwttokenstrategy.py │ │ │ └── test_tokens.py │ │ └── usersessions/ │ │ ├── __init__.py │ │ └── test_views.py │ ├── idp/ │ │ ├── __init__.py │ │ └── oidc/ │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── contrib/ │ │ │ ├── __init__.py │ │ │ ├── ninja/ │ │ │ │ ├── __init__.py │ │ │ │ └── test_views.py │ │ │ └── rest_framework/ │ │ │ ├── __init__.py │ │ │ └── test_views.py │ │ ├── internal/ │ │ │ ├── __init__.py │ │ │ └── oauthlib/ │ │ │ ├── __init__.py │ │ │ ├── test_request_validator.py │ │ │ └── test_utils.py │ │ ├── test_authorization.py │ │ ├── test_device.py │ │ ├── test_rp_initiated_logout.py │ │ ├── test_tokens.py │ │ ├── test_views.py │ │ └── test_wildcards.py │ ├── mfa/ │ │ ├── __init__.py │ │ ├── base/ │ │ │ ├── __init__.py │ │ │ ├── test_adapter.py │ │ │ ├── test_trust.py │ │ │ ├── test_trust_fingerprint.py │ │ │ └── test_views.py │ │ ├── recovery_codes/ │ │ │ ├── __init__.py │ │ │ ├── test_auth.py │ │ │ └── test_views.py │ │ ├── totp/ │ │ │ ├── __init__.py │ │ │ ├── test_unit.py │ │ │ └── test_views.py │ │ └── webauthn/ │ │ ├── __init__.py │ │ └── test_views.py │ ├── socialaccount/ │ │ ├── __init__.py │ │ ├── base.py │ │ ├── conftest.py │ │ ├── internal/ │ │ │ ├── __init__.py │ │ │ ├── test_jwtkit.py │ │ │ └── test_statekit.py │ │ ├── providers/ │ │ │ ├── __init__.py │ │ │ ├── agave/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── amazon/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── amazon_cognito/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── angellist/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── apple/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── asana/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── atlassian/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── auth0/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── authentiq/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── baidu/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── base/ │ │ │ │ ├── __init__.py │ │ │ │ └── test_provider.py │ │ │ ├── basecamp/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── battlenet/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── bitbucket_oauth2/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── bitly/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── box/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── cilogon/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── clever/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── coinbase/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── dataporten/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── daum/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── digitalocean/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── dingtalk/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── discogs/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── discord/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── disqus/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── douban/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── doximity/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── draugiem/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── drip/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── dropbox/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── dummy/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── dwolla/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── edmodo/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── edx/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── eventbrite/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── eveonline/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── evernote/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── exist/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── facebook/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── feedly/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── feishu/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── figma/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── fivehundredpx/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── flickr/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── foursquare/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── frontier/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── fxa/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── gitea/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── github/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── gitlab/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── globus/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── google/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── gumroad/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── hubic/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── hubspot/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── instagram/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── jupyterhub/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── kakao/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── lemonldap/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── lichess/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── line/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── linkedin_oauth2/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── mailchimp/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── mailcow/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── mailru/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── mediawiki/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── meetup/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── microsoft/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── miro/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── naver/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── netiq/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── nextcloud/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── notion/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── oauth2/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests/ │ │ │ │ └── test_views.py │ │ │ ├── odnoklassniki/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── okta/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── openid/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── openid_connect/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── openstreetmap/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── orcid/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── patreon/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── paypal/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── pinterest/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── pocket/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── questrade/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── quickbooks/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── reddit/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── robinhood/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── salesforce/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── saml/ │ │ │ │ ├── __init__.py │ │ │ │ ├── conftest.py │ │ │ │ └── tests.py │ │ │ ├── sharefile/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── shopify/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── slack/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── snapchat/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── soundcloud/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── spotify/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── stackexchange/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── steam/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── stocktwits/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── strava/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── stripe/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── telegram/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── tiktok/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── trainingpeaks/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── trello/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── tumblr/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── tumblr_oauth2/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── twentythreeandme/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── twitch/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── twitter/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── twitter_oauth2/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── untappd/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── vimeo/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── vimeo_oauth2/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── vk/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── wahoo/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── weibo/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── weixin/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── windowslive/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── xing/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── yahoo/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── yandex/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── ynab/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ ├── zoho/ │ │ │ │ ├── __init__.py │ │ │ │ └── tests.py │ │ │ └── zoom/ │ │ │ ├── __init__.py │ │ │ └── tests.py │ │ ├── test_adapter.py │ │ ├── test_connect.py │ │ ├── test_login.py │ │ ├── test_phone.py │ │ ├── test_registry.py │ │ ├── test_signup.py │ │ └── test_utils.py │ ├── test_utils.py │ └── usersessions/ │ ├── __init__.py │ ├── test_middleware.py │ └── test_views.py ├── conftest.py ├── mocking.py └── projects/ ├── account_only/ │ ├── __init__.py │ ├── settings.py │ ├── templates/ │ │ └── 429.html │ └── urls.py ├── common/ │ ├── __init__.py │ ├── account/ │ │ ├── __init__.py │ │ ├── urls.py │ │ └── views.py │ ├── adapters.py │ ├── headless/ │ │ ├── __init__.py │ │ ├── ninja/ │ │ │ ├── __init__.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ ├── rest_framework/ │ │ │ ├── __init__.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ └── urls.py │ ├── idp/ │ │ ├── __init__.py │ │ ├── ninja/ │ │ │ ├── __init__.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ ├── rest_framework/ │ │ │ ├── __init__.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ └── urls.py │ ├── phone_stub.py │ ├── settings.py │ ├── templates/ │ │ └── test_403_csrf.html │ └── urls.py ├── headless_only/ │ ├── __init__.py │ ├── settings.py │ └── urls.py ├── login_required_mw/ │ ├── __init__.py │ ├── middleware.py │ ├── settings.py │ ├── templates/ │ │ └── 429.html │ └── urls.py └── regular/ ├── __init__.py ├── settings.py └── urls.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dir-locals.el ================================================ ;;; This file contains project-specific emacs configuration ((python-mode . ((apheleia-formatter . (black isort))))) ================================================ FILE: .djlintrc ================================================ { "custom_blocks": "element,slot,setvar" } ================================================ FILE: .editorconfig ================================================ # http://editorconfig.org root = true [*] charset = utf-8 indent_style = space indent_size = 2 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true [*.ini] indent_size = 4 [*.py] indent_size = 4 [*.html] indent_size = 4 [Makefile] indent_style = tab indent_size = 8 [*.md] insert_final_newline = false trim_trailing_whitespace = false ================================================ FILE: .envrc ================================================ export DIRENV_WARN_TIMEOUT=20s eval "$(devenv direnvrc)" # The use_devenv function supports passing flags to the devenv command # For example: use devenv --impure --option services.postgres.enable:bool true use devenv ================================================ FILE: .gitea/ISSUE_TEMPLATE/discussion.md ================================================ --- name: "Discussion" about: "Overall discussions and general questions" title: "" assignees: [] labels: - "Discussion" --- ================================================ FILE: .gitea/ISSUE_TEMPLATE/issue.md ================================================ --- name: "Issue" about: "For cases clearly pointing to an issue in django-allauth" title: "" labels: "" assignees: [] --- ================================================ FILE: .gitea/pull_request_template.md ================================================ # Submitting Pull Requests ## General - [ ] Make sure you use [semantic commit messages](https://seesparkbox.com/foundry/semantic_commit_messages). Examples: `"fix(google): Fixed foobar bug"`, `"feat(accounts): Added foobar feature"`. - [ ] All Python code must formatted using Black, and clean from pep8 and isort issues. - [ ] JavaScript code should adhere to [StandardJS](https://standardjs.com). - [ ] If your changes are significant, please update `ChangeLog.rst`. - [ ] If your change is substantial, feel free to add yourself to `AUTHORS`. ## Provider Specifics In case you add a new provider: - [ ] Make sure unit tests are available. - [ ] Ensure your provider is tested by adding an entry over at `tests/projects/common/settings.py::INSTALLED_SOCIALACCOUNT_APPS`. - [ ] Provide provider specific documentation at `docs/socialaccount/providers/.rst`. - [ ] Link to your provider specific documentation at `docs/insocialaccount/providers/index.rst`. - [ ] Add an entry for your provider in the quickstart, over at `docs/installation/quickstart.rst::INSTALLED_APPS`. ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: pennersr patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] ================================================ FILE: .github/ISSUE_TEMPLATE/issue.md ================================================ --- name: "Issue (when not using Codeberg)" about: "\U0001F6D1 Please use https://codeberg.org/allauth/django-allauth/issues" title: '' labels: '' assignees: '' --- # 🛑 Stop The issue tracker has been moved to https://codeberg.org/allauth/django-allauth/issues. Please submit your issue there. ================================================ FILE: .github/SECURITY.md ================================================ # Security Policy Please report security issues only to security@allauth.org. ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: "github-actions" directory: "/" groups: GitHub_Actions: patterns: - "*" # Group all Actions updates into a single larger pull request schedule: interval: weekly ================================================ FILE: .github/pull_request_template.md ================================================ # 🛑 Stop The project has been moved to https://codeberg.org/allauth/django-allauth. Please submit your pull request there. ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI on: push: branches: [ main ] pull_request: branches: [ main ] jobs: test: runs-on: ubuntu-24.04 strategy: matrix: include: - python-version: "3.10" debian-version: trixie - python-version: "3.12" debian-version: trixie - python-version: "3.13" debian-version: trixie - python-version: "3.14" debian-version: trixie container: image: python:${{ matrix.python-version }}-${{ matrix.debian-version }} steps: - uses: actions/checkout@v6 - name: Install dependencies run: | apt-get update apt-get install -y --no-install-recommends gettext make - name: Install nox run: pip install nox - name: Run tests run: nox -x --session "test-${{ matrix.python-version }}" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} docs: runs-on: ubuntu-22.04 needs: [] strategy: matrix: python-version: [3.14] container: image: python:3.14-trixie steps: - uses: actions/checkout@v6 - name: Install nox run: pip install nox - name: Build docs run: nox -x --session "docs" lint: runs-on: ubuntu-22.04 needs: [] strategy: matrix: python-version: [3.14] container: image: python:3.14-trixie steps: - uses: actions/checkout@v6 - name: Install nox run: pip install nox - name: Run linter run: nox -x -t lint standardjs: runs-on: ubuntu-24.04 needs: [] strategy: matrix: python-version: [3.14] container: image: node:22-trixie steps: - uses: actions/checkout@v6 - name: Install standardjs dependencies run: make ci-install-standardjs - name: Run standardjs run: make standardjs ================================================ FILE: .gitignore ================================================ *.pyc *~ .idea .project .pydevproject *.geany docs/_build build dist *.egg* examples/**/local_settings.py node_modules/ # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* coverage.xml .mypy_cache/ .nox/ .ropeproject/* pep8.txt *.bak .#* \#* *.db *.tmp virtualenv .DS_Store *.prefs *.mo /.stfolder .direnv/ examples/react-spa/docker-compose.override.yml # Devenv .devenv* devenv.local.nix # direnv .direnv # pre-commit .pre-commit-config.yaml ================================================ FILE: .readthedocs.yaml ================================================ # Read the Docs configuration file for Sphinx projects # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the OS, Python version and other tools you might need build: os: ubuntu-22.04 tools: python: "3.11" # Build documentation in the "docs/" directory with Sphinx sphinx: configuration: docs/conf.py # Optional but recommended, declare the Python requirements required # to build your documentation # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - requirements: docs/requirements.txt ================================================ FILE: .woodpecker.yaml ================================================ when: - event: pull_request - event: [push, tag, manual] branch: main steps: test: image: python:${PYTHON_VERSION}-${DEBIAN_VERSION} commands: - apt-get update - apt-get install -y --no-install-recommends gettext make - pip install nox - nox -x --session "test-${PYTHON_VERSION}" docs: image: python:${PYTHON_VERSION}-${DEBIAN_VERSION} commands: - pip install nox - nox -x --session "docs" depends_on: [] when: - matrix: PYTHON_VERSION: 3.14 lint: image: python:${PYTHON_VERSION}-${DEBIAN_VERSION} commands: - pip install nox - nox -x -t lint depends_on: [] when: - matrix: PYTHON_VERSION: 3.14 standardjs: image: node:22-${DEBIAN_VERSION} commands: - make ci-install-standardjs - make standardjs depends_on: [] when: - matrix: PYTHON_VERSION: 3.14 matrix: include: # To cut down on CI pipeline duration, test oldest/latest Python only. - PYTHON_VERSION: 3.10 DEBIAN_VERSION: trixie - PYTHON_VERSION: 3.14 DEBIAN_VERSION: trixie ================================================ FILE: AUTHORS ================================================ django-allauth was started by Raymond Penners ( or @pennersr) in October 2010, inspired by and partly based on existing projects such as Pinax (account app), Django-Socialauth, django-socialregistration. # Contributors Aarni Koskela Aaron van Derlip Abhinav Johri Abhishek Kumar Jaiswal Adam Johnson Adam McKerlie Agustin Perez Paladini Ahmet Emre Aladağ Aiden Lu Aldiantoro Nugroho Alexander Gaevsky Alfredo Altamirano Andrean Franc Andrei Satsevich Andrew Chen Wang Andrey Akolpakov Andrey Balandin Andy Matthews Ani Vera Anna Sirota Anton Goncharov Antonin Delpeuch Aron Griffis Bas ten Berge Basil Shubin Ben Timby Benjamin Howes Benjamin Jorand Bhavani Ravi Biel Massot Björn Andersson Bojan Mihelac Brandon Kong Bruno Alla Chris Beaven Chris Davis Christian Carter Christopher Grebs Dan LaManna Dani Hodovic Daniel Eriksson Daniel Widerin David Ascher David Cain David D. Lowe David Evans David Friedman David Hummel Dimitris Tsimpitas Dmytro Litvinov Egor Poderyagin Eran Rundstein Eric Amador Eric Delord Fabio Caritas Barrionuevo da Luz Facundo Gaich Felipe Faria Filip Dobrovolný Francis Brito Frantisek Malina Fred Palmer Fábio Santos George Whewell Griffith Rees Guignard Javier Guilhem Saurel Guillaume Schurck Guillaume Vincent Guoyu Hao Haesung Park Hatem Nassrat Hyunwoo Shim Ian R-P Ignacio Ocampo Illia Volochii J. Erm J. Fernando Sánchez Jack Shedd Jakob Gerhard Martinussen James Rivett-Carnac James Thompson Jannis Leidel Jannis Vajen Jason Wallace Jeff Bowen Jeff Triplett Jeremy Satterfield Jerome Leclanche Jesse Gerard Brands Jihoon Park Jiyoon Ha Joe Vanderstelt Joel Fernandes John Bazik John Whitlock Jonas Aule JoonHwan Kim Josh Owen Josh Wright Joshua Butler Joshua Sorenson Julen Ruiz Aizpuru Justin Michalicek Justin Pogrob Karthikeyan Singaravelan Karun Shrestha Kevin Dice Kimsia Sim Koichi Harakawa Kun Liu Kyle Harrison Lee Semel Lev Predan Kowarski Luis Diego García Luiz Guilherme Pais dos Santos Luke Burden Luke Crouch Maksim Rukomoynikov Marcin Skarbek Marcin Spoczynski Marco Fucci Marjori Pomarole Markus Kaiserswerth Markus Thielen Martin Bächtold Matt Nishi-Broach Mauro Stettler Mikhail Mitiaev Morgante Pell Nariman Gharib Nathan Strobbe Nicolas Acosta Niklas A Emanuelsson Oleg Sergeev Patrick Paul Paul Juergen Fischer Paulo Eduardo Neves Pavel Oborin Pavel Savchenko Peter Bittner Peter Rowlands Peter Stein Philip John James Rabi Alam Radek Czajka Rense VanderHoek Rick Westera Robert Balfre Roberto Novaes Rod Xavier Bondoc Roman Tomjak Roumen Antonov Ryan Jarvis Ryan Verner Safwan Rahman Sam Solomon Sanghyeok Lee Seizan Shimazaki Serafeim Papastefanos Sergey Silaev Shane Rice Stephen Kent Stuart Ross Terry Jones Tiago Loureiro Tim Gates Tom Hacohen Tomas Babej Tomas Marcik Trey Corple Tuk Bredsdorff Udi Oron Vadim Meshcheryakov Veljko Jovanovic Victor Semionov Vlad Dmitrievich Volodymyr Yatsyk Vuong Nguyen Wendy Edwards Will Gordon Will Ross William Li Yaroslav Muravsky Youcef Mammar Yuri Kriachko Mohamed BEN MAKHLOUF ================================================ FILE: CONTRIBUTING.rst ================================================ Contributing to django-allauth ============================== .. begin-contributing Thank you for considering contributing to django-allauth! This document outlines the process for contributing to the project and sets up your development environment. Code of Conduct --------------- In the interest of fostering an open and welcoming community, we as contributors and maintainers pledge to make participation in our project a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. Getting Started --------------- Setting Up Your Development Environment ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are two primary ways to set up your development environment: Option 1: Standard Python Virtual Environment (Recommended for Beginners) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. **Install system dependencies** Before creating a virtual environment, you'll need to install some system dependencies: **On macOS:** .. code-block:: bash # Using Homebrew brew install libxml2 libxmlsec1 pkg-config openssl **On Ubuntu/Debian:** .. code-block:: bash sudo apt-get install libxml2-dev libxmlsec1-dev libxmlsec1-openssl pkg-config **On RHEL/CentOS/Fedora:** .. code-block:: bash sudo dnf install libxml2-devel xmlsec1-devel xmlsec1-openssl-devel libtool-ltdl-devel 2. **Create and activate a virtual environment** .. code-block:: bash # Create a virtual environment python -m venv virtualenv # Activate it # On Windows: virtualenv\Scripts\activate # On macOS/Linux: source virtualenv/bin/activate 3. **Install django-allauth in development mode** .. code-block:: bash # Install development dependencies pip install -r requirements-dev.txt Option 2: Using devenv/Nix (Recommended for Advanced Users) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you prefer a more isolated and reproducible development environment, you can use Nix-based `devenv `_: 1. **Install devenv** (If you don't have it already) Follow the `official installation instructions `_. 2. **Activate the developer environment** .. code-block:: bash # This will create an isolated environment with all required dependencies devenv shell Note: The first time you run this command, it may take a significant amount of time as it builds all dependencies. Subsequent launches will be much faster. Running Tests ------------- django-allauth uses a comprehensive test suite. You can run tests in several ways: Using pytest directly ~~~~~~~~~~~~~~~~~~~~~ .. code-block:: bash # Run all tests for the default setup pytest allauth/ # Run tests with a specific Django settings module pytest --ds=tests.projects.regular.settings tests/ # Run a specific test file pytest tests/apps/account/test_login.py Note, if you are using MacOS, using pip and get this error when run tests: .. code-block:: bash import xmlsec ImportError: dlopen( ... symbol not found in flat namespace '_xmlSecOpenSSLTransformHmacRipemd160GetKlass') You can try: .. code-block:: bash pip uninstall xmlsec lxml pip install --no-binary :all: xmlsec # Ref: https://github.com/xmlsec/python-xmlsec/issues/320 Using nox (recommended) ~~~~~~~~~~~~~~~~~~~~~~~ Nox automates testing across different Python and Django versions: .. code-block:: bash # List all available sessions nox --list # Run tests for a specific Python version nox -x --session "test-3.11" # Run tests for specific environment nox -x --session "test-3.11" --python 3.11 -- --ds=tests.projects.regular.settings tests/apps/account/test_login.py Run Code Quality Checks ~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: bash # Run all linting checks nox -t lint # Run specific check nox --session black nox --session isort nox --session flake8 nox --session bandit nox --session djlint Building Documentation ---------------------- Documentation is built using Sphinx: .. code-block:: bash # Build the documentation nox --session docs The built documentation will be available in the ``docs/_build/html`` directory. Development Workflow -------------------- 1. **Create a new branch for your feature or bugfix** .. code-block:: bash git checkout -b feature/your-feature-name 2. **Make your changes and add tests** All new features should include proper tests. 3. **Run tests locally to ensure everything passes** .. code-block:: bash nox -x --session "test-3.11" 4. **Run code quality checks** .. code-block:: bash nox -t lint 5. **Commit your changes with meaningful commit messages** 6. **Submit a pull request to the main repository** Pull Request Guidelines ----------------------- - Update documentation for significant changes - Add tests for new functionality - Ensure all tests pass - Follow the project's code style - Keep pull requests focused on a single topic - Write clear, descriptive commit messages Additional Resources -------------------- - `Official Documentation `_ - `Issue Tracker `_ - `Project Source `_ Thank you for your contributions! .. end-contributing ================================================ FILE: ChangeLog.rst ================================================ 65.15.0 (2025-03-09) ******************** .. note:: 💙 **Is django-allauth's authentication the entrance to your business?** Please consider supporting its continued development by becoming a sponsor at https://allauth.org/sponsors/. Your support helps keep this project thriving! Note worthy changes ------------------- - All user facing codes (e.g. those that the user needs to manually input over at password reset, email/phone verification, login code, OIDC device codes) now follow the recommendations over at `RFC 8628, Section 6.1 `_. It uses dashed codes, such as "WDJB-MJHT", by default. You can control the format of all codes via a new setting `ALLAUTH_USER_CODE_FORMAT``, or, adjust the format per use case via one of ``ACCOUNT_LOGIN_BY_CODE_FORMAT``, ``ACCOUNT_PHONE_VERIFICATION_CODE_FORMAT``, ``ACCOUNT_PASSWORD_RESET_BY_CODE_FORMAT``, ``ACCOUNT_EMAIL_VERIFICATION_BY_CODE_FORMAT``, ``IDP_OIDC_USER_CODE_FORMAT``. - Added optional support for requesting new login codes. See ``ACCOUNT_LOGIN_BY_CODE_SUPPORTS_RESEND``. Backwards incompatible changes ------------------------------ - Dropped support for Python 3.8 and 3.9. Both these Python versions are end-of-life. 65.14.3 (2026-02-13) ******************** Fixes ----- - Version 65.14.2 was not compatible with Python 3.8/3.9 due to use of an unsupported typing construct, fixed. 65.14.2 (2026-02-13) ******************** Security notice --------------- - Rate limiting and IP address detection: as Django applications cannot reliably determine client IP addresses out of the box, you must override ``get_client_ip()`` to match your deployment architecture. If you omitted to do so, the default implementation trusted ``X-Forwarded-For``, which can be spoofed to bypass rate limits. Now, ``X-Forwarded-For`` is distrusted by default. You must either configure ``ALLAUTH_TRUSTED_PROXY_COUNT``, rely on ``ALLAUTH_TRUSTED_CLIENT_IP_HEADER``, or override ``get_client_ip()``. Thanks to Ayato Shitomi for reporting. 65.14.1 (2026-02-07) ******************** Fixes ----- - When using ``ACCOUNT_CHANGE_EMAIL = True``, if the user initiating the change email process had no verified email address, ``user.email`` would still reflect the old email address while the verification process was pending. Security notice --------------- - SAML: When IdP initiated SSO was enabled (it is by default disabled), any URL found in the SAML ``RelayState`` parameter would be used to redirect to, potentially redirecting the authenticated user to a wrong site. Thanks to Ayato Shitomi and Funabiki Keisuke for reporting. 65.14.0 (2026-01-17) ******************** Note worthy changes ------------------- - Steam: the provider now supports initiating headless logins per redirect. - Shopify: if ``email_verified`` is present in the user payload, it will be used to mark the email address retrieved as verified accordingly. - IdP: added support for JWT based access tokens (see ``IDP_OIDC_ACCESS_TOKEN_FORMAT``). - IdP: added support for pointing to a custom userinfo endpoint (see ``IDP_OIDC_USERINFO_ENDPOINT``) - For OpenID Connect providers, you can now configure the field to be used as the account ID by setting ``"uid_field"`` in the relevant ``SocialApp.settings``. - Headless: the JWT algorithm is now configurable, supporting HS256. Fixes ----- - IdP: Access tokens without a user attached (client credentials) were no longer recognized in DRF/Ninja endpoints. - ``requests`` sessions are now disposed of after use to avoid resource leaks. 65.13.1 (2025-11-20) ******************** Note worthy changes ------------------- - Django 6.0 is now officially supported. Fixes ----- - Internal imports related to headless token strategies were causing (harmless) deprecation warnings, fixed. - Pending social signups stored in the session by allauth versions prior to 65.5.0 are not resumable by newer versions. This could cause 500s while upgrading, fixed. - Headless: the reauthentication-required response in the OpenAPI specification was wrongly nested and did not match the actual implementation, fixed. 65.13.0 (2025-10-31) ******************** Note worthy changes ------------------- - IdP: Added support for RP-Initiated Logout. - Headless: added JWT token strategy. - Added support for "Trust this browser?" functionality for logging in by code. See ``ACCOUNT_LOGIN_BY_CODE_TRUST_ENABLED``. - OpenID Connect: to avoid issues with client IDs containing colons, ``client_secret_post`` is now preferred above ``client_secret_basic``. Security notice --------------- - Both Okta and NetIQ were using ``preferred_username`` as the identifier for third-party provider accounts. That value may be mutable and should therefore be avoided for authorization decisions. The providers are now using ``sub`` instead. - IdP: marking a user as ``is_active=False`` after having handed tokens for that user while the account was still active had no effect. Fixed -- the access/refresh tokens are now rejected. Thanks to Joshua Rogers for reporting this and the previous issue. Backwards incompatible changes ------------------------------ - Headless now requires the ``headless`` extra to be installed. For example: ``pip install django-allauth[headless]``. - Okta and NetIQ: see the security notice on Okta and NetIQ. Already existing ``SocialAccount`` records will no longer be linked due to the switch to ``sub``. You will need to manually handle this situation either, by populating ``SocialAccount.uid`` based on ``sub`` located in ``SocialAccount.extra_data``,or, if you are absolutely certain the security notice is of no concern for your use case, by setting ``"uid_field": "preferred_username"`` in the relevant ``SocialApp.settings``. 65.12.1 (2025-10-16) ******************** Security notice --------------- - There was a flaw in the email verification process when using ``ACCOUNT_CHANGE_EMAIL = True``. If you are using this configuration, you are advised to upgrade as soon as possible. Note that the default value is ``False``. 65.12.0 (2025-10-05) ******************** Note worthy changes ------------------- - Updated VK urls from "vk.com" to "vk.ru". - Added new socialaccount provider: Discogs. - MediaWiki: you can now setup a custom user agent to avoid getting blocked, see: https://phabricator.wikimedia.org/T400119 - IdP: Added optional support for wildcards in redirect URIs and CORS origins. To support this, a new field was added to the ``Client`` model. Therefore, you need to run migrations for the ``allauth.idp.oidc`` app. - Headless: an email address that was in the process of being verified by code was presented in the list of email address, but could not be deleted. Now, a ``DELETE`` will actually abort the process, effectively removing the pending email address from the list. 65.11.2 (2025-09-09) ******************** Fixes ----- - OpenID Connect: the OpenID Connect provider was using the wrong key lookup mechanism, resulting in login failures. 65.11.1 (2025-08-27) ******************** Security notice --------------- - If you configured password to be optional (e.g. using ``ACCOUNT_SIGNUP_FIELDS = ["email*", "password1"]``), then accounts would be created having a blank password instead of an unusable password. If you were using this configuration then you may need to manually set an unusable password for accounts created. 65.11.0 (2025-08-15) ******************** Note worthy changes ------------------- - OpenID Connect: using ``fetch_userinfo=False`` you can now skip the additional call to the userinfo endpoint. Instead, the ID token will be used. Fixes ----- - Headless: passwordless signup was not supported, fixed. - Headless: when serializing the user nested dataclasses, as well as optional types ended up as ``string`` type, fixed. - When signing up with a social account no authentication record was added to the session, fixed. Backwards incompatible changes ------------------------------ - OpenID Connect: the ID token and userinfo (when present) are now stored in ``SocialAccount.extra_data``, below the respective ``"id_token"`` and ``"userinfo"`` keys. Compatibility with ``extra_data`` from existing accounts that do not have this new structure is retained. However, if your own project expects ``extra_data`` to be in a certain format this change may impact you. 65.10.0 (2025-07-10) ******************** Note worthy changes ------------------- - IdP: Added support for the device authorization grant. - Headless: custom user payloads can now be properly reflected in the OpenAPI specification by provider a user ``dataclass``. See the newly introduced ``get_user_dataclass()`` and ``user_as_dataclass()`` adapter methods. - Added a new signal (``authentication_step_completed``) that is emitted when an individual authentication step is completed. - MFA: Added a setting (``MFA_ALLOW_UNVERIFIED_EMAIL``) to allow unverified email addresses in combination with MFA. Backwards incompatible changes ------------------------------ - Soundcloud: as per https://developers.soundcloud.com/blog/urn-num-to-string, the provider now uses the user ``urn`` instead of the ``id`` as the ID for social accounts. This change is backward incompatible. Instructions on how to migrate existing social accounts can be found in the allauth provider documentation for SoundCloud. Fixes ----- - Phone: The recently added support for phone (SMS) authentication did fully integrate with third-party provider signups. For example, whether or not the phone number is required was not respected during signup. Fixed. - IdP: Token revocation failed when a single ``token_type_hint`` was passed, fixed. - The ``verified_email_required`` decorator did not support email verification by code. Additionally, it did not rate limit verification emails in case of GET requests. Both are fixed. - The account adapter ``clean_email()`` method was not called when a social account auto signup took place, fixed. 65.9.0 (2025-06-01) ******************* Note worthy changes ------------------- - Added ``allauth.idp`` to the project, offering out of the box OpenID Connect provider support, as well as integration with Django REST framework and Django Ninja. - Headless: the OpenAPI specification now more accurately reflects single client configurations set via ``HEADLESS_CLIENTS``. When a single client is active, the redundant ``{client}`` path parameter and its global definition are removed from the generated schema. 65.8.1 (2025-05-21) ******************* Fixes ----- - Fixed a compatibility issue with the newly released fido2 2.0.0 package. Security notice --------------- - After a successful login, the rate limits for that login were cleared, allowing a succesful login on a specific IP address to be used as a means to clear the login failed rate limit for that IP address. Fixed. 65.8.0 (2025-05-08) ******************* Note worthy changes ------------------- - Fixed VK (a.k.a VK ID) social account provider. Improved its documentation. - Added optional support for requesting new email/phone verification codes during signup. See ``ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_RESEND`` and ``ACCOUNT_PHONE_VERIFICATION_SUPPORTS_RESEND``. - Added optional support for changing your email or phone at the verification stage while signing up. See ``ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_CHANGE`` and ``ACCOUNT_PHONE_VERIFICATION_SUPPORTS_CHANGE``. - Added support for Mailcow OAuth2. 65.7.0 (2025-04-03) ******************* Note worthy changes ------------------- - Officially support Django 5.2. - Headless: the URL to the OpenID configuration of the provider is now exposed in the provider configuration. Fixes ----- - Headless: when multiple login methods were enabled (e.g. both username and email), the login endpoint would incorrectly return a 400 ``invalid_login``. Fixed. 65.6.0 (2025-03-27) ******************* Note worthy changes ------------------- - MFA: Added support for "Trust this browser?" functionality, which presents users with MFA enabled the choice to trust their browser allowing them to skip authenticating per MFA on each login. Fixes ----- - A check is in place to verify that ``ACCOUNT_LOGIN_METHODS`` is aligned with ``ACCOUNT_SIGNUP_FIELDS``. The severity level of that check has now been lowered from "critical" to "warning", as there may be valid use cases for configuring a login method that you are not able to sign up with. This check (``account.W001``) can be silenced using Django's ``SILENCED_SYSTEM_CHECKS``. - The setting ``ACCOUNT_LOGIN_ON_PASSWORD_RESET = True`` was not respected when using password reset by code. 65.5.0 (2025-03-14) ******************* Note worthy changes ------------------- - Added support for phone (SMS) authentication. - Added support for resetting passwords by code, instead of a link (``ACCOUNT_PASSWORD_RESET_BY_CODE_ENABLED``). - Added support for Tumblr OAuth2. - Simplified signup form configuration. The following settings all controlled signup form: ``ACCOUNT_EMAIL_REQUIRED``, ``ACCOUNT_USERNAME_REQUIRED``, ``ACCOUNT_SIGNUP_EMAIL_ENTER_TWICE``, ``ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE``. This setup had its issues. For example, when email was not required it was still available as an optional field, whereas the username field disappeared when not required. Also, for phone/SMS support, additional settings would have been required. The settings are now all deprecated, and replaced by one new setting: ``ACCOUNT_SIGNUP_FIELDS``, which can be configured to e.g. ``['username*', 'email', 'password1*', 'password2*']`` to indicate which fields are present and required (``'*'``). This change is performed in a backwards compatible manner. - Headless: if, while signing up using a third-party provider account, there is insufficient information received from the provider to automatically complete the signup process, an additional step is needed to complete the missing data before the user is fully signed up and authenticated. You can now perform a ``GET`` request to ``/_allauth/{client}/v1/auth/provider/signup`` to obtain information on the pending signup. - Headless: OpenID Connect providers now support token authentication. - The "Forgot your password?" help text can now be more easily customized by providing your own ``"account/password_reset_help_text.html"`` template. - Removed inline scripts, so that it becomes possible to use a strong Content Security Policy. - Headless: The OpenAPI specification now dynamically reflects the ``ACCOUNT_SIGNUP_FIELDS`` configuration, as well as any custom fields you have in ``ACCOUNT_SIGNUP_FORM_CLASS``. - Added official support for Python 3.13. Fixes ----- - Headless: In case you had multiple apps of the same provider configured, you could run into a ``MultipleObjectsReturned``. Fixed. 65.4.1 (2025-02-07) ******************* Fixes ----- - To make way for a future ``"phone"`` method, ``AUTHENTICATION_METHOD`` was removed in favor of a new ``LOGIN_METHODS``. While this change was done in a backwards compatible manner within allauth scope, other packages accessing ``allauth.account.app_settings.AUTHENTICATION_METHOD`` would break. Fixed. 65.4.0 (2025-02-06) ******************* Note worthy changes ------------------- - The setting ``ACCOUNT_AUTHENTICATION_METHOD: str`` (with values ``"username"``, ``"username_email"``, ``"email"``) has been replaced by ``ACCOUNT_LOGIN_METHODS: set[str]``. which is a set of values including ``"username"`` or ``"email"``. This change is performed in a backwards compatible manner. - Headless: when ``HEADLESS_SERVE_SPECIFICATION`` is set to ``True``, the API specification will be served dynamically, over at ``/_allauth/openapi.(yaml|json|html)``. The ``HEADLESS_SPECIFICATION_TEMPLATE_NAME`` can be configured to choose between Redoc (``"headless/spec/redoc_cdn.html"``) and Swagger ( (``"headless/spec/swagger_cdn.html"``). - Headless: added a new setting, ``HEADLESS_CLIENTS`` which you can use to limit the types of API clients (app/browser). - Headless: expanded the React SPA example to showcase integration with Django Ninja as well as Django REST framework. - Headless: added out of the box support for being able to use the headless session tokens with Django Ninja and Django REST framework. ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2010-2026 Raymond Penners and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ PYTHON = python .PHONY: usage usage: @echo 'Usage: make [target]' .PHONY: po po: ( cd allauth ; $(PYTHON) ../manage.py makemessages -a -e html,txt,py ) .PHONY: mo mo: ( cd allauth ; $(PYTHON) ../manage.py compilemessages ) .PHONY: isort isort: isort --check-only --diff allauth/ tests/ .PHONY: bandit bandit: bandit -q -c pyproject.toml -r allauth/ .PHONY: black black: black --check -q . .PHONY: test test: pytest allauth/ .PHONY: djlint djlint: djlint --quiet --check allauth examples .PHONY: flake8 flake8: flake8 allauth .PHONY: qa qa: validate-api-spec mypy djlint bandit black isort flake8 .PHONY: mypy mypy: mypy allauth/ .PHONY: validate-api-spec validate-api-spec: @if command -v swagger-cli >/dev/null 2>&1; then \ swagger-cli validate allauth/headless/spec/doc/openapi.yaml; \ else \ echo "WARNING: swagger-cli not found in PATH."; \ fi .PHONY: ci ci: woodpecker-cli exec .woodpecker.yaml .PHONY: standardjs standardjs: find ./allauth -name '*.js' | xargs ./node_modules/.bin/standard --ignore allauth/mfa/static/mfa/js/webauthn-json.js .PHONY: docs docs: $(MAKE) -C docs html .PHONY: ci-install-standardjs ci-install-standardjs: npm install standard --no-lockfile --no-progress --non-interactive --silent .PHONY: clean clean: -rm -rf build/ django_allauth.egg-info/ .PHONY: dist dist: clean mo python -m build --sdist python -m build --wheel ================================================ FILE: README.rst ================================================ ========================== Welcome to django-allauth! ========================== .. image:: https://codeberg.org/allauth/allauth.org/raw/commit/da3b56390e1b18eaec09b05cd89dfa7812212dfc/content/news/2024/04/website-redesign/logo-light.png :target: https://allauth.org :align: right :alt: django-allauth logo :width: 250px .. |ci| image:: https://img.shields.io/github/actions/workflow/status/pennersr/django-allauth/ci.yml.svg :target: https://github.com/pennersr/django-allauth/actions .. |pypi| image:: https://img.shields.io/pypi/v/django-allauth :target: https://pypi.python.org/pypi/django-allauth .. |cov| image:: https://img.shields.io/coverallsCoverage/github/pennersr/django-allauth :alt: Coverage Status :target: https://coveralls.io/r/pennersr/django-allauth .. |btc| image:: https://img.shields.io/badge/bitcoin-donate-yellow :target: https://blockchain.info/address/1AJXuBMPHkaDCNX2rwAy34bGgs7hmrePEr .. |liberapay| image:: https://img.shields.io/liberapay/receives/pennersr :target: https://en.liberapay.com/pennersr .. |pystyle| image:: https://img.shields.io/badge/code_style-pep8-green :target: https://www.python.org/dev/peps/pep-0008/ .. |jsstyle| image:: https://img.shields.io/badge/code_style-standard-brightgreen :target: http://standardjs.com .. |editor| image:: https://img.shields.io/badge/editor-emacs-purple :target: https://www.gnu.org/software/emacs/ .. |i18n| image:: https://img.shields.io/weblate/progress/allauth :target: https://hosted.weblate.org/projects/allauth/django-allauth/ .. |pypidl| image:: https://img.shields.io/pypi/dm/django-allauth :target: https://pypistats.org/packages/django-allauth :alt: PyPI - Downloads .. |djangodemo| image:: https://img.shields.io/badge/%E2%96%B6_demo-Django_project-red :target: https://django.demo.allauth.org/ :alt: View Django Demo .. |reactdemo| image:: https://img.shields.io/badge/%E2%96%B6_demo-React_SPA-red :target: https://react.demo.allauth.org/ :alt: View React SPA Demo |ci| |pypi| |cov| |btc| |liberapay| |pystyle| |jsstyle| |editor| |i18n| |pypidl| |djangodemo| |reactdemo| Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication. Home page https://allauth.org/ Source code https://codeberg.org/allauth/django-allauth Issue Tracker https://codeberg.org/allauth/django-allauth/issues Documentation https://docs.allauth.org/en/latest/ Stack Overflow https://stackoverflow.com/questions/tagged/django-allauth Demo https://django.demo.allauth.org and https://react.demo.allauth.org Translations https://hosted.weblate.org/projects/allauth/django-allauth/ .. end-welcome Rationale ========= .. begin-rationale Most existing Django apps that address the problem of social authentication unfortunately focus only on one dimension - the social. Most developers end up integrating another app in order to support authentication flows that are locally generated. This approach creates a development gap between local and social authentication flows. It has remained an issue in spite of numerous common scenarios that both require. For example, an email address passed along by an OpenID provider may not be verified. Therefore, prior to hooking up an OpenID account to a local account the email address must be verified. This essentially is one of many use cases that mandate email verification to be present in both worlds. Integrating both is a humongous and tedious process. It is not as simple as adding one social authentication app, and one local account registration app to your ``INSTALLED_APPS`` list. This inadequacy is the reason for this project's existence -- to offer a fully integrated authentication app that allows for both local and social authentication, with flows that just work, beautifully! .. end-rationale Features ======== .. begin-features **🔑 Comprehensive account functionality** Supports multiple authentication schemes (e.g. login by user name, or by email), as well as multiple strategies for account verification (ranging from none to mandatory email verification). **👥 Social Login** Login using external identity providers, supporting any *Open ID Connect compatible* provider, many *OAuth 1.0/2.0* providers, as well as custom protocols such as, for example, *Telegram* authentication. **💼 Enterprise ready** Supports SAML 2.0, which is often used in a B2B context. **🕵️ Battle-tested** The package has been out in the open since 2010. It is in use by many commercial companies whose business depends on it and has hence been subjected to various penetration testing attempts. **⏳Rate limiting** When you expose an authentication-enabled web service to the internet, it is important to be prepared for potential brute force attempts. Therefore, rate limiting is enabled out of the box. **🔒 Private** Many sites leak information. For example, on many sites you can check whether someone you know has an account by input their email address into the password forgotten form, or trying to signup with it. We offer account enumeration prevention, making it impossible to tell whether or not somebody already has an account. **🧩 Customizable** As a developer, you have the flexibility to customize the core functionality according to your specific requirements. By employing the adapter pattern, you can effortlessly introduce interventions at the desired points to deviate from the standard behavior. This level of customization empowers you to tailor the software to meet your unique needs and preferences. **⚙️ Configuration** The required consumer keys and secrets for interacting with Facebook, X (Twitter) and the likes can be configured using regular settings, or, can be configured in the database via the Django admin. Here, optional support for the Django sites framework is available, which is helpful for larger multi-domain projects, but also allows for easy switching between a development (localhost) and production setup without messing with your settings and database. .. end-features Commercial Support ================== .. begin-support Commercial support is available. If you find certain functionality missing, or require assistance on your project(s), please contact us: info@intenct.nl. .. end-support ================================================ FILE: allauth/__init__.py ================================================ r""" _ ___ __ __ .___________. __ __ /\| |/\ / \ | | | | | || | | | \ ` ' / / ^ \ | | | | `---| |----`| |__| | |_ _| / /_\ \ | | | | | | | __ | / , . \ / _____ \ | `--' | | | | | | | \/|_|\//__/ \__\ \______/ |__| |__| |__| """ VERSION = (65, 15, 0, "final", 0) __title__ = "django-allauth" __version_info__ = VERSION __version__ = ".".join(map(str, VERSION[:3])) + ( f"-{VERSION[3]}{VERSION[4] or ''}" if VERSION[3] != "final" else "" ) __author__ = "Raymond Penners" __license__ = "MIT" __copyright__ = "Copyright 2010-2026 Raymond Penners and contributors" ================================================ FILE: allauth/account/__init__.py ================================================ ================================================ FILE: allauth/account/adapter.py ================================================ import html import inspect import json import warnings from http import HTTPStatus from urllib.parse import urlparse from django.conf import settings from django.contrib import messages from django.contrib.auth import ( authenticate, get_backends, get_user_model, login as django_login, logout as django_logout, ) from django.contrib.auth.models import AbstractUser from django.contrib.auth.password_validation import ( MinimumLengthValidator, validate_password, ) from django.contrib.sites.shortcuts import get_current_site from django.core.exceptions import FieldDoesNotExist, PermissionDenied from django.core.mail import EmailMessage, EmailMultiAlternatives from django.http import HttpRequest, HttpResponse, HttpResponseRedirect from django.http.request import validate_host from django.shortcuts import resolve_url from django.template import TemplateDoesNotExist from django.template.loader import render_to_string from django.urls import reverse from django.utils import timezone from django.utils.crypto import get_random_string from django.utils.encoding import force_str from django.utils.translation import gettext_lazy as _ from allauth import app_settings as allauth_settings from allauth.account import app_settings, signals from allauth.core import context from allauth.core.internal import ratelimit from allauth.core.internal.adapter import BaseAdapter from allauth.core.internal.cryptokit import generate_user_code from allauth.core.internal.httpkit import ( HTTP_USER_AGENT_MAX_LENGTH, get_client_ip, headed_redirect_response, is_headless_request, ) from allauth.utils import generate_unique_username, import_attribute class DefaultAccountAdapter(BaseAdapter): """The adapter class allows you to override various functionality of the ``allauth.account`` app. To do so, point ``settings.ACCOUNT_ADAPTER`` to your own class that derives from ``DefaultAccountAdapter`` and override the behavior by altering the implementation of the methods according to your own needs. """ error_messages = { "account_inactive": _("This account is currently inactive."), "cannot_remove_primary_email": _( "You cannot remove your primary email address." ), "duplicate_email": _( "This email address is already associated with this account." ), "email_password_mismatch": _( "The email address and/or password you specified are not correct." ), "phone_password_mismatch": _( "The phone number and/or password you specified are not correct." ), "email_taken": _("A user is already registered with this email address."), "enter_current_password": _("Please type your current password."), "incorrect_code": _("Incorrect code."), "incorrect_password": _("Incorrect password."), "invalid_or_expired_key": _("Invalid or expired key."), "invalid_login": _("Invalid login."), "invalid_password_reset": _("The password reset token was invalid."), "max_email_addresses": _("You cannot add more than %d email addresses."), "phone_taken": _("A user is already registered with this phone number."), "too_many_login_attempts": _( "Too many failed login attempts. Try again later." ), "unknown_email": _("The email address is not assigned to any user account."), "unknown_phone": _("The phone number is not assigned to any user account."), "unverified_primary_email": _("Your primary email address must be verified."), "username_blacklisted": _( "Username can not be used. Please use other username." ), "username_password_mismatch": _( "The username and/or password you specified are not correct." ), "username_taken": AbstractUser._meta.get_field("username").error_messages[ "unique" ], "select_only_one": _("Please select only one."), "same_as_current": _("The new value must be different from the current one."), "rate_limited": _("Be patient, you are sending too many requests."), } def stash_verified_email(self, request, email): request.session["account_verified_email"] = email def unstash_verified_email(self, request): ret = request.session.get("account_verified_email") request.session["account_verified_email"] = None return ret def is_email_verified(self, request, email): """ Checks whether or not the email address is already verified beyond allauth scope, for example, by having accepted an invitation before signing up. """ ret = False verified_email = request.session.get("account_verified_email") if verified_email: ret = verified_email.lower() == email.lower() return ret def can_delete_email(self, email_address) -> bool: """ Returns whether or not the given email address can be deleted. """ from allauth.account.models import EmailAddress if not email_address.pk: return True has_other = ( EmailAddress.objects.filter(user_id=email_address.user_id) .exclude(pk=email_address.pk) .exists() ) login_by_email = app_settings.LOGIN_METHODS == {app_settings.LoginMethod.EMAIL} if email_address.primary: if has_other: # Don't allow, let the user mark one of the others as primary # first. return False elif login_by_email: # Last email & login is by email, prevent dangling account. return False return True elif has_other: # Account won't be dangling. return True elif login_by_email: # This is the last email. return False else: return True def format_email_subject(self, subject) -> str: """ Formats the given email subject. """ prefix = app_settings.EMAIL_SUBJECT_PREFIX if prefix is None: site = get_current_site(context.request) prefix = f"[{site.name}] " return prefix + force_str(subject) def get_from_email(self): """ This is a hook that can be overridden to programmatically set the 'from' email address for sending emails """ return settings.DEFAULT_FROM_EMAIL def render_mail(self, template_prefix, email, context, headers=None): """ Renders an email to `email`. `template_prefix` identifies the email that is to be sent, e.g. "account/email/email_confirmation" """ to = [email] if isinstance(email, str) else email subject = render_to_string(f"{template_prefix}_subject.txt", context) # remove superfluous line breaks subject = " ".join(subject.splitlines()).strip() subject = self.format_email_subject(subject) from_email = self.get_from_email() bodies = {} html_ext = app_settings.TEMPLATE_EXTENSION for ext in [html_ext, "txt"]: try: template_name = f"{template_prefix}_message.{ext}" bodies[ext] = render_to_string( template_name, context, globals()["context"].request, ).strip() except TemplateDoesNotExist: if ext == "txt" and not bodies: # We need at least one body raise if "txt" in bodies: msg = EmailMultiAlternatives( subject, bodies["txt"], from_email, to, headers=headers ) if html_ext in bodies: msg.attach_alternative(bodies[html_ext], "text/html") else: msg = EmailMessage( subject, bodies[html_ext], from_email, to, headers=headers ) msg.content_subtype = "html" # Main content is now text/html return msg def send_mail(self, template_prefix: str, email: str, context: dict) -> None: request = globals()["context"].request ctx = { "request": request, "email": email, "current_site": get_current_site(request), } ctx.update(context) msg = self.render_mail(template_prefix, email, ctx) msg.send() def get_signup_redirect_url(self, request): """ Returns the default URL to redirect to directly after signing up. """ return resolve_url(app_settings.SIGNUP_REDIRECT_URL) def get_login_redirect_url(self, request): """ Returns the default URL to redirect to after logging in. Note that URLs passed explicitly (e.g. by passing along a `next` GET parameter) take precedence over the value returned here. """ assert request.user.is_authenticated # nosec url = getattr(settings, "LOGIN_REDIRECT_URLNAME", None) if url: warnings.warn( "LOGIN_REDIRECT_URLNAME is deprecated, simply" " use LOGIN_REDIRECT_URL with a URL name", DeprecationWarning, ) else: url = settings.LOGIN_REDIRECT_URL return resolve_url(url) def get_logout_redirect_url(self, request): """ Returns the URL to redirect to after the user logs out. Note that this method is also invoked if you attempt to log out while no users is logged in. Therefore, request.user is not guaranteed to be an authenticated user. """ return resolve_url(app_settings.LOGOUT_REDIRECT_URL) def get_email_verification_redirect_url(self, email_address): """ The URL to return to after email verification. """ get_url = getattr(self, "get_email_confirmation_redirect_url", None) if get_url: # Deprecated. return get_url(self.request) if self.request.user.is_authenticated: if app_settings.EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL: return app_settings.EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL else: return self.get_login_redirect_url(self.request) else: return app_settings.EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL def get_password_change_redirect_url(self, request): """ The URL to redirect to after a successful password change/set. NOTE: Not called during the password reset flow. """ return reverse("account_change_password") def is_open_for_signup(self, request): """ Checks whether or not the site is open for signups. Next to simply returning True/False you can also intervene the regular flow by raising an ImmediateHttpResponse """ return True def new_user(self, request): """ Instantiates a new User instance. """ user = get_user_model()() return user def populate_username(self, request, user): """ Fills in a valid username, if required and missing. If the username is already present it is assumed to be valid (unique). """ from .utils import user_email, user_field, user_username first_name = user_field(user, "first_name") last_name = user_field(user, "last_name") email = user_email(user) username = user_username(user) if app_settings.USER_MODEL_USERNAME_FIELD: user_username( user, username or self.generate_unique_username( [first_name, last_name, email, username, "user"] ), ) def generate_unique_username(self, txts, regex=None): return generate_unique_username(txts, regex) def save_user(self, request, user, form, commit=True): """ Saves a new `User` instance using information provided in the signup form. """ from .utils import user_email, user_field, user_username data = form.cleaned_data first_name = data.get("first_name") last_name = data.get("last_name") email = data.get("email") username = data.get("username") user_email(user, email) user_username(user, username) if first_name: user_field(user, "first_name", first_name) if last_name: user_field(user, "last_name", last_name) if "password1" in data: password = data["password1"] elif "password" in data: password = data["password"] else: password = None if password: user.set_password(password) else: user.set_unusable_password() self.populate_username(request, user) if commit: user.save() if getattr(form, "_has_phone_field", False): phone = form.cleaned_data.get("phone") if phone: self.set_phone(user, phone, False) return user def clean_username(self, username, shallow=False): """ Validates the username. You can hook into this if you want to (dynamically) restrict what usernames can be chosen. """ for validator in app_settings.USERNAME_VALIDATORS: validator(username) # TODO: Add regexp support to USERNAME_BLACKLIST username_blacklist_lower = [ ub.lower() for ub in app_settings.USERNAME_BLACKLIST ] if username.lower() in username_blacklist_lower: raise self.validation_error("username_blacklisted") # Skipping database lookups when shallow is True, needed for unique # username generation. if not shallow: from .utils import filter_users_by_username if filter_users_by_username(username).exists(): raise self.validation_error("username_taken") return username def clean_email(self, email: str) -> str: """ Validates an email value. You can hook into this if you want to (dynamically) restrict what email addresses can be chosen. """ return email def clean_password(self, password, user=None): """ Validates a password. You can hook into this if you want to restric the allowed password choices. """ min_length = app_settings.PASSWORD_MIN_LENGTH if min_length: MinimumLengthValidator(min_length).validate(password) validate_password(password, user) return password def clean_phone(self, phone: str) -> str: """ Validates a phone number. You can hook into this if you want to (dynamically) restrict what phone numbers can be chosen. """ return phone def validate_unique_email(self, email): return email def add_message( self, request, level, message_template=None, message_context=None, extra_tags="", message=None, ): """ Wrapper of `django.contrib.messages.add_message`, that reads the message text from a template. """ if is_headless_request(request): return if "django.contrib.messages" in settings.INSTALLED_APPS: if message: messages.add_message(request, level, message, extra_tags=extra_tags) return try: if message_context is None: message_context = {} escaped_message = render_to_string( message_template, message_context, context.request, ).strip() if escaped_message: message = html.unescape(escaped_message) messages.add_message(request, level, message, extra_tags=extra_tags) except TemplateDoesNotExist: pass def ajax_response(self, request, response, redirect_to=None, form=None, data=None): resp = {} status = response.status_code if redirect_to: status = HTTPStatus.OK resp["location"] = redirect_to if form: if request.method == "POST": if form.is_valid(): status = HTTPStatus.OK else: status = HTTPStatus.BAD_REQUEST else: status = HTTPStatus.OK resp["form"] = self.ajax_response_form(form) if hasattr(response, "render"): response.render() resp["html"] = response.content.decode("utf8") if data is not None: resp["data"] = data return HttpResponse( json.dumps(resp), status=status, content_type="application/json" ) def ajax_response_form(self, form): form_spec = { "fields": {}, "field_order": [], "errors": form.non_field_errors(), } for field in form: field_spec = { "label": force_str(field.label), "value": field.value(), "help_text": force_str(field.help_text), "errors": [force_str(e) for e in field.errors], "widget": { "attrs": { k: force_str(v) for k, v in field.field.widget.attrs.items() } }, } form_spec["fields"][field.html_name] = field_spec form_spec["field_order"].append(field.html_name) return form_spec def pre_login( self, request, user, *, email_verification, signal_kwargs, email, signup, redirect_url, ): if not user.is_active: return self.respond_user_inactive(request, user) def post_login( self, request, user, *, email_verification, signal_kwargs, email, signup, redirect_url, ): from .utils import get_login_redirect_url if is_headless_request(request): from allauth.headless.base.response import AuthenticationResponse response = AuthenticationResponse(request) else: response = HttpResponseRedirect( get_login_redirect_url(request, redirect_url, signup=signup) ) if signal_kwargs is None: signal_kwargs = {} signals.user_logged_in.send( sender=user.__class__, request=request, response=response, user=user, **signal_kwargs, ) self.add_message( request, messages.SUCCESS, "account/messages/logged_in.txt", {"user": user}, ) return response def login(self, request, user): # HACK: This is not nice. The proper Django way is to use an # authentication backend if not hasattr(user, "backend"): from .auth_backends import AuthenticationBackend backends = get_backends() backend = None for b in backends: if isinstance(b, AuthenticationBackend): # prefer our own backend backend = b break elif not backend and hasattr(b, "get_user"): # Pick the first valid one backend = b backend_path = f"{backend.__module__}.{backend.__class__.__name__}" user.backend = backend_path django_login(request, user) def logout(self, request): django_logout(request) def confirm_email(self, request, email_address): """ Marks the email address as confirmed on the db """ from allauth.account.internal.flows import email_verification return email_verification.verify_email(request, email_address) def set_password(self, user, password) -> None: """ Sets the password for the user. """ user.set_password(password) user.save() def get_user_search_fields(self): ret = [] User = get_user_model() candidates = [ app_settings.USER_MODEL_USERNAME_FIELD, "first_name", "last_name", "email", ] for candidate in candidates: try: User._meta.get_field(candidate) ret.append(candidate) except FieldDoesNotExist: pass return ret def is_safe_url(self, url): from django.utils.http import url_has_allowed_host_and_scheme # get_host already validates the given host, so no need to check it again allowed_hosts = {context.request.get_host()} | set(settings.ALLOWED_HOSTS) # Include hosts derived from CSRF_TRUSTED_ORIGINS trusted_hosts = { urlparse(origin).netloc for origin in settings.CSRF_TRUSTED_ORIGINS } allowed_hosts.update(trusted_hosts) # ALLOWED_HOSTS supports wildcards, and subdomains using a '.' prefix. # But, `url_has_allowed_host_and_scheme()` doesn't support that. So, # let's check the domain using the ALLOWED_HOSTS logic, and if valid, # add it as allowed so that we can then call # `url_has_allowed_host_and_scheme()`. parsed_host = urlparse(url).netloc if parsed_host: if validate_host(parsed_host, allowed_hosts): allowed_hosts.add(parsed_host) return url_has_allowed_host_and_scheme(url, allowed_hosts=allowed_hosts) def send_password_reset_mail(self, user, email, context): """ Method intended to be overridden in case you need to customize the logic used to determine whether a user is permitted to request a password reset. For example, if you are enforcing something like "social only" authentication in your app, you may want to intervene here by checking `user.has_usable_password` """ return self.send_mail("account/email/password_reset_key", email, context) def get_reset_password_from_key_url(self, key): """ Method intended to be overridden in case the password reset email needs to be adjusted. """ from allauth.account.internal import flows return flows.password_reset.get_reset_password_from_key_url(self.request, key) def get_email_confirmation_url(self, request, emailconfirmation): """Constructs the email confirmation (activation) url. Note that if you have architected your system such that email confirmations are sent outside of the request context `request` can be `None` here. """ from allauth.account.internal import flows return flows.email_verification.get_email_verification_url( request, emailconfirmation ) def should_send_confirmation_mail(self, request, email_address, signup) -> bool: return True def send_account_already_exists_mail(self, email: str) -> None: from allauth.account.internal import flows signup_url = flows.signup.get_signup_url(context.request) password_reset_url = flows.password_reset.get_reset_password_url( context.request ) ctx = { "signup_url": signup_url, "password_reset_url": password_reset_url, } self.send_mail("account/email/account_already_exists", email, ctx) def send_confirmation_mail(self, request, emailconfirmation, signup): ctx = { "user": emailconfirmation.email_address.user, } if app_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED: ctx.update({"code": emailconfirmation.key}) else: ctx.update( { "key": emailconfirmation.key, "activate_url": self.get_email_confirmation_url( request, emailconfirmation ), } ) if signup: email_template = "account/email/email_confirmation_signup" else: email_template = "account/email/email_confirmation" self.send_mail(email_template, emailconfirmation.email_address.email, ctx) def respond_user_inactive(self, request, user): return headed_redirect_response("account_inactive") def respond_email_verification_sent(self, request, user): return headed_redirect_response("account_email_verification_sent") def _get_login_attempts_cache_key(self, request, **credentials): site = get_current_site(request) login = credentials.get("email", credentials.get("username", "")).lower() return f"{site.domain}:{login}" def _delete_login_attempts_cached_email(self, request, **credentials): cache_key = self._get_login_attempts_cache_key(request, **credentials) # Here, we wipe the login failed rate limit, completely. This is safe, # as we only do this on a succesful password reset, which is rate limited # on itself (e.g. sending of email etc.). ratelimit.clear( request, config=app_settings.RATE_LIMITS, action="login_failed", key=cache_key, ) def _rollback_login_failed_rl_usage(self) -> None: usage = getattr(self, "_login_failed_rl_usage", None) if usage: usage.rollback() def pre_authenticate(self, request, **credentials): cache_key = self._get_login_attempts_cache_key(request, **credentials) self._login_failed_rl_usage = ratelimit.consume( request, config=app_settings.RATE_LIMITS, action="login_failed", key=cache_key, ) if not self._login_failed_rl_usage: raise self.validation_error("too_many_login_attempts") def authenticate(self, request, **credentials): """Only authenticates, does not actually login. See `login`""" from allauth.account.auth_backends import AuthenticationBackend self.pre_authenticate(request, **credentials) AuthenticationBackend.unstash_authenticated_user() user = authenticate(request, **credentials) alt_user = AuthenticationBackend.unstash_authenticated_user() user = user or alt_user if user: # On a succesful login, we cannot just wipe the login failed rate # limit. That consists of 2 parts, a per IP limit, and, a per # key(email) limit. Wiping it completely would allow an attacker to # insert periodic successful logins during a brute force # process. So instead, we are rolling back our consumption. self._rollback_login_failed_rl_usage() else: self.authentication_failed(request, **credentials) return user def authentication_failed(self, request, **credentials): pass def reauthenticate(self, user, password): from allauth.account.models import EmailAddress from allauth.account.utils import user_username credentials = {"password": password} username = user_username(user) if username: credentials["username"] = username email = EmailAddress.objects.get_primary_email(user) if email: credentials["email"] = email if app_settings.LoginMethod.PHONE in app_settings.LOGIN_METHODS: phone_verified = self.get_phone(user) if phone_verified: credentials["phone"] = phone_verified[0] reauth_user = self.authenticate(context.request, **credentials) return reauth_user is not None and reauth_user.pk == user.pk def is_ajax(self, request): return any( [ request.META.get("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest", request.content_type == "application/json", request.META.get("HTTP_ACCEPT") == "application/json", ] ) def get_client_ip(self, request) -> str: """ Returns the IP address of the client. """ ip = get_client_ip(request) if not ip: raise PermissionDenied("Unable to determine client IP address") return ip def get_http_user_agent(self, request: HttpRequest) -> str: return request.META.get("HTTP_USER_AGENT", "Unspecified") def generate_emailconfirmation_key(self, email): key = get_random_string(64).lower() return key def get_login_stages(self): ret = [] ret.append("allauth.account.stages.LoginByCodeStage") ret.append("allauth.account.stages.PhoneVerificationStage") ret.append("allauth.account.stages.EmailVerificationStage") if allauth_settings.MFA_ENABLED: from allauth.mfa import app_settings as mfa_settings ret.append("allauth.mfa.stages.AuthenticateStage") if mfa_settings._TRUST_STAGE_ENABLED: ret.append("allauth.mfa.stages.TrustStage") if mfa_settings.PASSKEY_SIGNUP_ENABLED: ret.append("allauth.mfa.webauthn.stages.PasskeySignupStage") return ret def get_reauthentication_methods(self, user): """The order of the methods returned matters. The first method is the default when using the `@reauthentication_required` decorator. """ from allauth.account.internal.flows.reauthentication import ( get_reauthentication_flows, ) flow_by_id = {f["id"]: f for f in get_reauthentication_flows(user)} ret = [] if "reauthenticate" in flow_by_id: entry = { "id": "reauthenticate", "description": _("Use your password"), "url": reverse("account_reauthenticate"), } ret.append(entry) if "mfa_reauthenticate" in flow_by_id: types = flow_by_id["mfa_reauthenticate"]["types"] if "recovery_codes" in types or "totp" in types: entry = { "id": "mfa_reauthenticate", "description": _("Use authenticator app or code"), "url": reverse("mfa_reauthenticate"), } ret.append(entry) if "webauthn" in types: entry = { "id": "mfa_reauthenticate:webauthn", "description": _("Use a security key"), "url": reverse("mfa_reauthenticate_webauthn"), } ret.append(entry) return ret def send_notification_mail(self, template_prefix, user, context=None, email=None): from allauth.account.models import EmailAddress if not app_settings.EMAIL_NOTIFICATIONS: return if not email: email = EmailAddress.objects.get_primary_email(user) if not email: return ctx = { "timestamp": timezone.now(), "ip": self.get_client_ip(self.request), "user_agent": self.get_http_user_agent(self.request)[ :HTTP_USER_AGENT_MAX_LENGTH ], } if context: ctx.update(context) self.send_mail(template_prefix, email, ctx) def generate_login_code(self) -> str: """ Generates a new login code. """ return generate_user_code(**app_settings.LOGIN_BY_CODE_FORMAT) def generate_password_reset_code(self) -> str: """ Generates a new password reset code. """ return generate_user_code(**app_settings.PASSWORD_RESET_BY_CODE_FORMAT) def generate_email_verification_code(self) -> str: """ Generates a new email verification code. """ return generate_user_code(**app_settings.EMAIL_VERIFICATION_BY_CODE_FORMAT) def generate_phone_verification_code(self, *, user, phone: str) -> str: """ Generates a new phone verification code. """ return generate_user_code(**app_settings.PHONE_VERIFICATION_CODE_FORMAT) def _generate_phone_verification_code_compat(self, *, user, phone: str) -> str: sig = inspect.signature(self.generate_phone_verification_code) if len(sig.parameters) == 0: warnings.warn( "generate_phone_verification_code(self) is deprecated, use generate_phone_verification_code(self, *, user, phone)", DeprecationWarning, ) return self.generate_phone_verification_code() # type: ignore[call-arg] return self.generate_phone_verification_code(user=user, phone=phone) def is_login_by_code_required(self, login) -> bool: """ Returns whether or not login-by-code is required for the given login. """ from allauth.account import authentication method = None records = authentication.get_authentication_records(self.request) if records: method = records[-1]["method"] if method == "code": return False value = app_settings.LOGIN_BY_CODE_REQUIRED if isinstance(value, bool): return value if not value: return False return method is None or method in value def phone_form_field(self, **kwargs): """ Returns a form field used to input phone numbers. """ from allauth.account.fields import PhoneField return PhoneField(**kwargs) def send_unknown_account_sms(self, phone: str, **kwargs) -> None: """ In case enumeration prevention is enabled, and, a verification code is requested for an unlisted phone number, this method is invoked to send a text explaining that no account is on file. """ pass def send_account_already_exists_sms(self, phone: str) -> None: pass def send_verification_code_sms(self, user, phone: str, code: str, **kwargs): """ Sends a verification code. """ raise NotImplementedError @property def _has_phone_impl(self) -> bool: """ Checks whether the phone number adapter is fully implemented. """ methods = ( "send_verification_code_sms", "set_phone", "get_phone", "set_phone_verified", "get_user_by_phone", ) return all( getattr(self.__class__, method) != getattr(DefaultAccountAdapter, method) for method in methods ) def set_phone(self, user, phone: str, verified: bool): """ Sets the phone number (and verified status) for the given user. """ raise NotImplementedError def get_phone(self, user) -> tuple[str, bool] | None: """ Returns the phone number stored for the given user. A tuple of the phone number itself, and whether or not the phone number was verified is returned. """ raise NotImplementedError def set_phone_verified(self, user, phone: str): """ Marks the specified phone number for the given user as verified. Note that the user is already expected to have the phone number attached to the account. """ raise NotImplementedError def get_user_by_phone(self, phone: str): """ Looks up a user given the specified phone number. Returns ``None`` if no user was found. """ raise NotImplementedError def get_adapter(request=None) -> DefaultAccountAdapter: return import_attribute(app_settings.ADAPTER)(request) ================================================ FILE: allauth/account/admin.py ================================================ from django.contrib import admin, messages from django.utils.translation import gettext_lazy as _ from allauth.account import app_settings, signals from allauth.account.adapter import get_adapter from allauth.account.models import EmailAddress, EmailConfirmation class EmailAddressAdmin(admin.ModelAdmin): list_display = ("email", "user", "primary", "verified") list_filter = ("primary", "verified") search_fields = [] raw_id_fields = ("user",) actions = ["make_verified"] def get_search_fields(self, request): base_fields = get_adapter().get_user_search_fields() return ["email"] + list(map(lambda a: f"user__{a}", base_fields)) def make_verified(self, request, queryset): for email_address in queryset.filter(verified=False).iterator(): if email_address.set_verified(): signals.email_confirmed.send( sender=EmailAddress, request=request, email_address=email_address, ) self.message_user( request, _("Marked {email} as verified.").format(email=email_address.email), level=messages.SUCCESS, ) else: self.message_user( request, _("Failed to mark {email} as verified.").format( email=email_address.email ), level=messages.ERROR, ) make_verified.short_description = _("Mark selected email addresses as verified") # type: ignore[attr-defined] class EmailConfirmationAdmin(admin.ModelAdmin): list_display = ("email_address", "created", "sent", "key") list_filter = ("sent",) raw_id_fields = ("email_address",) if not app_settings.EMAIL_CONFIRMATION_HMAC: admin.site.register(EmailConfirmation, EmailConfirmationAdmin) admin.site.register(EmailAddress, EmailAddressAdmin) ================================================ FILE: allauth/account/app_settings.py ================================================ import warnings from enum import Enum from allauth import app_settings as allauth_settings from allauth.core.internal.cryptokit import UserCodeFormat class AppSettings: class AuthenticationMethod(str, Enum): USERNAME = "username" EMAIL = "email" USERNAME_EMAIL = "username_email" class LoginMethod(str, Enum): USERNAME = "username" EMAIL = "email" PHONE = "phone" class EmailVerificationMethod(str, Enum): # After signing up, keep the user account inactive until the email # address is verified MANDATORY = "mandatory" # Allow login with unverified email (email verification is # still sent) OPTIONAL = "optional" # Don't send email verification mails during signup NONE = "none" def __init__(self, prefix): self.prefix = prefix def _setting(self, name, dflt): from allauth.utils import get_setting return get_setting(self.prefix + name, dflt) @property def PREVENT_ENUMERATION(self): return self._setting("PREVENT_ENUMERATION", True) @property def DEFAULT_HTTP_PROTOCOL(self): return self._setting("DEFAULT_HTTP_PROTOCOL", "http").lower() @property def EMAIL_CONFIRMATION_EXPIRE_DAYS(self): """ Determines the expiration date of email confirmation mails (# of days) """ from django.conf import settings return self._setting( "EMAIL_CONFIRMATION_EXPIRE_DAYS", getattr(settings, "EMAIL_CONFIRMATION_DAYS", 3), ) @property def EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL(self): """ The URL to redirect to after a successful email confirmation, in case of an authenticated user """ return self._setting("EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL", None) @property def EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL(self): """ The URL to redirect to after a successful email confirmation, in case no user is logged in """ from django.conf import settings return self._setting( "EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL", settings.LOGIN_URL ) @property def EMAIL_REQUIRED(self): """ The user is required to hand over an email address when signing up """ warnings.warn( "app_settings.EMAIL_REQUIRED is deprecated, use: app_settings.SIGNUP_FIELDS['email']['required']", stacklevel=3, ) email = self.SIGNUP_FIELDS.get("email") return email and email["required"] @property def EMAIL_VERIFICATION(self): """ See email verification method """ ret = self._setting("EMAIL_VERIFICATION", self.EmailVerificationMethod.OPTIONAL) # Deal with legacy (boolean based) setting if ret is True: ret = self.EmailVerificationMethod.MANDATORY elif ret is False: ret = self.EmailVerificationMethod.OPTIONAL return self.EmailVerificationMethod(ret) @property def EMAIL_VERIFICATION_BY_CODE_ENABLED(self): return self._setting("EMAIL_VERIFICATION_BY_CODE_ENABLED", False) @property def EMAIL_VERIFICATION_BY_CODE_MAX_ATTEMPTS(self): return self._setting("EMAIL_VERIFICATION_BY_CODE_MAX_ATTEMPTS", 3) @property def EMAIL_VERIFICATION_BY_CODE_TIMEOUT(self): return self._setting("EMAIL_VERIFICATION_BY_CODE_TIMEOUT", 15 * 60) @property def EMAIL_VERIFICATION_MAX_CHANGE_COUNT(self) -> int: """ The maximum number of times the email can be changed after signup at the email veriication stage. """ v = self._setting("EMAIL_VERIFICATION_SUPPORTS_CHANGE", False) if isinstance(v, bool): v = 2 if v else 0 return v @property def EMAIL_VERIFICATION_MAX_RESEND_COUNT(self) -> int: """ The maximum number of times the user can request a new email verification code. """ v = self._setting("EMAIL_VERIFICATION_SUPPORTS_RESEND", False) if isinstance(v, bool): v = 2 if v else 0 return v @property def MAX_EMAIL_ADDRESSES(self): return self._setting("MAX_EMAIL_ADDRESSES", None) @property def CHANGE_EMAIL(self): return self._setting("CHANGE_EMAIL", False) @property def AUTHENTICATION_METHOD(self): warnings.warn( "app_settings.AUTHENTICATION_METHOD is deprecated, use: app_settings.LOGIN_METHODS", stacklevel=3, ) methods = self.LOGIN_METHODS if self.LoginMethod.EMAIL in methods and self.LoginMethod.USERNAME in methods: return "username_email" elif self.LoginMethod.EMAIL in methods: return "email" elif self.LoginMethod.USERNAME in methods: return "username" else: raise NotADirectoryError @property def LOGIN_METHODS(self) -> frozenset[LoginMethod]: methods = self._setting("LOGIN_METHODS", None) if methods is None: auth_method = self._setting( "AUTHENTICATION_METHOD", self.AuthenticationMethod.USERNAME ) if auth_method == self.AuthenticationMethod.USERNAME_EMAIL: methods = {self.LoginMethod.EMAIL, self.LoginMethod.USERNAME} else: methods = {self.LoginMethod(auth_method)} return frozenset([self.LoginMethod(m) for m in methods]) @property def EMAIL_MAX_LENGTH(self): """ Adjust max_length of email addresses """ return self._setting("EMAIL_MAX_LENGTH", 254) @property def PHONE_VERIFICATION_ENABLED(self): return self._setting("PHONE_VERIFICATION_ENABLED", True) @property def PHONE_VERIFICATION_MAX_ATTEMPTS(self): return self._setting("PHONE_VERIFICATION_MAX_ATTEMPTS", 3) @property def PHONE_VERIFICATION_MAX_CHANGE_COUNT(self) -> int: """ The maximum number of times the phone number can be changed after signup at the phone number verification stage. """ v = self._setting("PHONE_VERIFICATION_SUPPORTS_CHANGE", False) if isinstance(v, bool): v = 2 if v else 0 return v @property def PHONE_VERIFICATION_MAX_RESEND_COUNT(self) -> int: """ The maximum number of times the user can request a new phone number verification code. """ v = self._setting("PHONE_VERIFICATION_SUPPORTS_RESEND", False) if isinstance(v, bool): v = 2 if v else 0 return v @property def PHONE_VERIFICATION_TIMEOUT(self): return self._setting("PHONE_VERIFICATION_TIMEOUT", 15 * 60) @property def UNIQUE_EMAIL(self): """ Enforce uniqueness of email addresses """ return self._setting("UNIQUE_EMAIL", True) @property def SIGNUP_EMAIL_ENTER_TWICE(self): """ Signup email verification """ warnings.warn( "app_settings.SIGNUP_EMAIL_ENTER_TWICE is deprecated, use: 'email2' in app_settings.SIGNUP_FIELDS", stacklevel=3, ) return "email2" in self.SIGNUP_FIELDS @property def SIGNUP_PASSWORD_ENTER_TWICE(self): """ Signup password verification """ warnings.warn( "app_settings.SIGNUP_PASSWORD_ENTER_TWICE is deprecated, use: 'password2' in app_settings.SIGNUP_FIELDS", stacklevel=3, ) return "password2" in self.SIGNUP_FIELDS @property def SIGNUP_REDIRECT_URL(self): from django.conf import settings return self._setting("SIGNUP_REDIRECT_URL", settings.LOGIN_REDIRECT_URL) @property def PASSWORD_MIN_LENGTH(self): """ Minimum password Length """ from django.conf import settings ret = None if not settings.AUTH_PASSWORD_VALIDATORS: ret = self._setting("PASSWORD_MIN_LENGTH", 6) return ret @property def RATE_LIMITS(self): rls = self._setting("RATE_LIMITS", {}) if rls is False: return {} attempts_amount = self._setting("LOGIN_ATTEMPTS_LIMIT", 5) attempts_timeout = self._setting("LOGIN_ATTEMPTS_TIMEOUT", 60 * 5) login_failed_rl = None if attempts_amount and attempts_timeout: login_failed_rl = f"10/m/ip,{attempts_amount}/{attempts_timeout}s/key" if self.EMAIL_VERIFICATION_BY_CODE_ENABLED: confirm_email_rl = "1/10s/key" else: cooldown = self._setting("EMAIL_CONFIRMATION_COOLDOWN", 3 * 60) confirm_email_rl = None if cooldown: confirm_email_rl = f"1/{cooldown}s/key" ret = { # Change password view (for users already logged in) "change_password": "5/m/user", # nosec # Change phone number "change_phone": "1/m/user", # Email management (e.g. add, remove, change primary) "manage_email": "10/m/user", # Request a password reset, global rate limit per IP "reset_password": "20/m/ip,5/m/key", # Reauthentication for users already logged in "reauthenticate": "10/m/user", # Password reset (the view the password reset email links to). "reset_password_from_key": "20/m/ip", # Signups. "signup": "20/m/ip", # Logins. "login": "30/m/ip", # Request a login code: key is the email. "request_login_code": "20/m/ip,3/m/key", # Logins. "login_failed": login_failed_rl, # Verify email (to be renamed to verify_email) "confirm_email": confirm_email_rl, # Verify phone "verify_phone": "1/30s/key,3/m/ip", } ret.update(rls) return ret @property def EMAIL_SUBJECT_PREFIX(self): """ Subject-line prefix to use for email messages sent """ return self._setting("EMAIL_SUBJECT_PREFIX", None) @property def SIGNUP_FORM_CLASS(self): """ Signup form """ return self._setting("SIGNUP_FORM_CLASS", None) @property def SIGNUP_FORM_HONEYPOT_FIELD(self): """ Honeypot field name. Empty string or ``None`` will disable honeypot behavior. """ return self._setting("SIGNUP_FORM_HONEYPOT_FIELD", None) @property def SIGNUP_FIELDS(self) -> dict: fields = self._setting("SIGNUP_FIELDS", None) if not fields: fields = [] username = self._setting("USERNAME_REQUIRED", True) email = self._setting("EMAIL_REQUIRED", False) email2 = self._setting("SIGNUP_EMAIL_ENTER_TWICE", False) password2 = self._setting( "SIGNUP_PASSWORD_ENTER_TWICE", self._setting("SIGNUP_PASSWORD_VERIFICATION", True), ) if email: fields.append("email*") else: fields.append("email") if email2: fields.append("email2*" if email else "email2") if username: fields.append("username*") fields.append("password1*") if password2: fields.append("password2*") ret = {} for field in fields: f, req, _ = field.partition("*") ret[f] = {"required": bool(req)} return ret @property def USERNAME_REQUIRED(self): """ The user is required to enter a username when signing up """ warnings.warn( "app_settings.USERNAME_REQUIRED is deprecated, use: app_settings.SIGNUP_FIELDS['username']['required']", stacklevel=3, ) username = self.SIGNUP_FIELDS.get("username") return username and username["required"] @property def USERNAME_MIN_LENGTH(self): """ Minimum username Length """ return self._setting("USERNAME_MIN_LENGTH", 1) @property def USERNAME_BLACKLIST(self): """ List of usernames that are not allowed """ return self._setting("USERNAME_BLACKLIST", []) @property def PASSWORD_INPUT_RENDER_VALUE(self): """ render_value parameter as passed to PasswordInput fields """ return self._setting("PASSWORD_INPUT_RENDER_VALUE", False) @property def ADAPTER(self): return self._setting("ADAPTER", "allauth.account.adapter.DefaultAccountAdapter") @property def CONFIRM_EMAIL_ON_GET(self): return self._setting("CONFIRM_EMAIL_ON_GET", False) @property def AUTHENTICATED_LOGIN_REDIRECTS(self): return self._setting("AUTHENTICATED_LOGIN_REDIRECTS", True) @property def LOGIN_ON_EMAIL_CONFIRMATION(self): """ Automatically log the user in once they confirmed their email address """ return self._setting("LOGIN_ON_EMAIL_CONFIRMATION", False) @property def LOGIN_ON_PASSWORD_RESET(self): """ Automatically log the user in immediately after resetting their password. """ return self._setting("LOGIN_ON_PASSWORD_RESET", False) @property def LOGOUT_REDIRECT_URL(self): from django.conf import settings return self._setting("LOGOUT_REDIRECT_URL", settings.LOGOUT_REDIRECT_URL or "/") @property def LOGOUT_ON_GET(self): return self._setting("LOGOUT_ON_GET", False) @property def LOGOUT_ON_PASSWORD_CHANGE(self): return self._setting("LOGOUT_ON_PASSWORD_CHANGE", False) @property def USER_MODEL_USERNAME_FIELD(self): return self._setting("USER_MODEL_USERNAME_FIELD", "username") @property def USER_MODEL_EMAIL_FIELD(self): return self._setting("USER_MODEL_EMAIL_FIELD", "email") @property def SESSION_COOKIE_AGE(self): """ Deprecated -- use Django's settings.SESSION_COOKIE_AGE instead """ from django.conf import settings return self._setting("SESSION_COOKIE_AGE", settings.SESSION_COOKIE_AGE) @property def SESSION_REMEMBER(self): """ Controls the life time of the session. Set to `None` to ask the user ("Remember me?"), `False` to not remember, and `True` to always remember. """ return self._setting("SESSION_REMEMBER", None) @property def TEMPLATE_EXTENSION(self): """ A string defining the template extension to use, defaults to `html`. """ return self._setting("TEMPLATE_EXTENSION", "html") @property def FORMS(self): return self._setting("FORMS", {}) @property def EMAIL_CONFIRMATION_HMAC(self): return self._setting("EMAIL_CONFIRMATION_HMAC", True) @property def SALT(self): return self._setting("SALT", "account") @property def PRESERVE_USERNAME_CASING(self): return self._setting("PRESERVE_USERNAME_CASING", True) @property def USERNAME_VALIDATORS(self): from django.contrib.auth import get_user_model from django.core.exceptions import ImproperlyConfigured from allauth.utils import import_attribute path = self._setting("USERNAME_VALIDATORS", None) if path: ret = import_attribute(path) if not isinstance(ret, list): raise ImproperlyConfigured( "ACCOUNT_USERNAME_VALIDATORS is expected to be a list" ) else: if self.USER_MODEL_USERNAME_FIELD is not None: ret = ( get_user_model() ._meta.get_field(self.USER_MODEL_USERNAME_FIELD) .validators ) else: ret = [] return ret @property def PASSWORD_RESET_BY_CODE_ENABLED(self): return self._setting("PASSWORD_RESET_BY_CODE_ENABLED", False) @property def PASSWORD_RESET_BY_CODE_MAX_ATTEMPTS(self): return self._setting("PASSWORD_RESET_BY_CODE_MAX_ATTEMPTS", 3) @property def PASSWORD_RESET_BY_CODE_TIMEOUT(self): return self._setting("PASSWORD_RESET_BY_CODE_TIMEOUT", 3 * 60) @property def PASSWORD_RESET_TOKEN_GENERATOR(self): from allauth.account.forms import EmailAwarePasswordResetTokenGenerator from allauth.utils import import_attribute token_generator_path = self._setting("PASSWORD_RESET_TOKEN_GENERATOR", None) if token_generator_path is not None: token_generator = import_attribute(token_generator_path) else: token_generator = EmailAwarePasswordResetTokenGenerator return token_generator @property def EMAIL_UNKNOWN_ACCOUNTS(self): return self._setting("EMAIL_UNKNOWN_ACCOUNTS", True) @property def REAUTHENTICATION_TIMEOUT(self): return self._setting("REAUTHENTICATION_TIMEOUT", 300) @property def EMAIL_NOTIFICATIONS(self): return self._setting("EMAIL_NOTIFICATIONS", False) @property def REAUTHENTICATION_REQUIRED(self): return self._setting("REAUTHENTICATION_REQUIRED", False) @property def LOGIN_BY_CODE_ENABLED(self): return self._setting("LOGIN_BY_CODE_ENABLED", False) @property def LOGIN_BY_CODE_TRUST_ENABLED(self): return self._setting("LOGIN_BY_CODE_TRUST_ENABLED", False) @property def LOGIN_BY_CODE_MAX_ATTEMPTS(self): return self._setting("LOGIN_BY_CODE_MAX_ATTEMPTS", 3) @property def LOGIN_BY_CODE_MAX_RESEND_COUNT(self) -> int: """ The maximum number of times the user can request a new login code. """ v = self._setting("LOGIN_BY_CODE_SUPPORTS_RESEND", False) if isinstance(v, bool): v = 2 if v else 0 return v @property def LOGIN_BY_CODE_TIMEOUT(self): return self._setting("LOGIN_BY_CODE_TIMEOUT", 3 * 60) @property def LOGIN_TIMEOUT(self): """ The maximum allowed time (in seconds) for a login to go through the various login stages. This limits, for example, the time span that the 2FA stage remains available. """ return self._setting("LOGIN_TIMEOUT", 15 * 60) @property def LOGIN_BY_CODE_REQUIRED(self) -> bool | set[str]: """ When enabled (in case of ``True``), every user logging in is required to input a login confirmation code sent by email. Alternatively, you can specify a set of authentication methods (``"password"``, ``"mfa"``, or ``"socialaccount"``) for which login codes are required. """ value = self._setting("LOGIN_BY_CODE_REQUIRED", False) if isinstance(value, bool): return value return set(value) @property def LOGIN_BY_CODE_FORMAT(self) -> UserCodeFormat: """ Controls the format of the login code. """ return self._setting("LOGIN_BY_CODE_FORMAT", allauth_settings.USER_CODE_FORMAT) @property def PHONE_VERIFICATION_CODE_FORMAT(self) -> UserCodeFormat: """ Controls the format of the phone verification code. """ return self._setting( "PHONE_VERIFICATION_CODE_FORMAT", allauth_settings.USER_CODE_FORMAT ) @property def PASSWORD_RESET_BY_CODE_FORMAT(self) -> UserCodeFormat: """ Controls the format of the password reset code. """ return self._setting( "PASSWORD_RESET_BY_CODE_CODE_FORMAT", allauth_settings.USER_CODE_FORMAT ) @property def EMAIL_VERIFICATION_BY_CODE_FORMAT(self) -> UserCodeFormat: """ Controls the format of the email verification code. """ return self._setting( "EMAIL_VERIFICATION_BY_CODE_FORMAT", allauth_settings.USER_CODE_FORMAT ) _app_settings = AppSettings("ACCOUNT_") def __getattr__(name): # See https://peps.python.org/pep-0562/ return getattr(_app_settings, name) ================================================ FILE: allauth/account/apps.py ================================================ from django.apps import AppConfig from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.utils.translation import gettext_lazy as _ from allauth import app_settings class AccountConfig(AppConfig): name = "allauth.account" verbose_name = _("Accounts") default_auto_field = app_settings.DEFAULT_AUTO_FIELD or "django.db.models.AutoField" def ready(self): from allauth.account import checks # noqa required_mw = "allauth.account.middleware.AccountMiddleware" if required_mw not in settings.MIDDLEWARE: raise ImproperlyConfigured( f"{required_mw} must be added to settings.MIDDLEWARE" ) ================================================ FILE: allauth/account/auth_backends.py ================================================ from threading import local from django.contrib.auth import get_user_model from django.contrib.auth.backends import ModelBackend from allauth.account.adapter import get_adapter from allauth.account.app_settings import LoginMethod from . import app_settings from .utils import filter_users_by_email, filter_users_by_username _stash = local() class AuthenticationBackend(ModelBackend): def authenticate(self, request, **credentials): password = credentials.get("password") if not password: return None self._did_check_password = False user = self._authenticate(request, **credentials) if not self._did_check_password: self._mitigate_timing_attack(password) return user def _authenticate(self, request, **credentials): password = credentials.get("password") username = credentials.get("username") if username: if LoginMethod.EMAIL in app_settings.LOGIN_METHODS: # Username/email ambiguity: even though allauth will pass along # `email` explicitly, other apps may not respect this. For example, # when using django-tastypie basic authentication, the login is # always passed as `username`. So let's play nice with other apps # and use username as fallback. user = self._authenticate_by_email(username, password) if user: return user user = self._authenticate_by_username(username, password) if user: return user email = credentials.get("email") if email: user = self._authenticate_by_email(email, password) if user: return user phone = credentials.get("phone") if phone: user = self._authenticate_by_phone(phone, password) if user: return user return None def _authenticate_by_phone(self, phone: str, password: str): if not phone or LoginMethod.PHONE not in app_settings.LOGIN_METHODS: return None adapter = get_adapter() user = adapter.get_user_by_phone(phone) return self._check_password(user, password) def _authenticate_by_username(self, username: str, password: str): if ( (LoginMethod.USERNAME not in app_settings.LOGIN_METHODS) or (not app_settings.USER_MODEL_USERNAME_FIELD) or not username ): return None user = filter_users_by_username(username).first() return self._check_password(user, password) def _authenticate_by_email( self, email: str, password: str, ): if not email or LoginMethod.EMAIL not in app_settings.LOGIN_METHODS: return None users = filter_users_by_email(email, prefer_verified=True) for user in users: if self._check_password(user, password): return user return None def _mitigate_timing_attack(self, password): get_user_model()().set_password(password) def _check_password(self, user, password): if not user: return None self._did_check_password = True ok = user.check_password(password) if ok: ok = self.user_can_authenticate(user) if not ok: self._stash_user(user) return user if ok else None @classmethod def _stash_user(cls, user): """Now, be aware, the following is quite ugly, let me explain: Even if the user credentials match, the authentication can fail because Django's default ModelBackend calls user_can_authenticate(), which checks `is_active`. Now, earlier versions of allauth did not do this and simply returned the user as authenticated, even in case of `is_active=False`. For allauth scope, this does not pose a problem, as these users are properly redirected to an account inactive page. This does pose a problem when the allauth backend is used in a different context where allauth is not responsible for the login. Then, by not checking on `user_can_authenticate()` users will allow to become authenticated whereas according to Django logic this should not be allowed. In order to preserve the allauth behavior while respecting Django's logic, we stash a user for which the password check succeeded but `user_can_authenticate()` failed. In the allauth authentication logic, we can then unstash this user and proceed pointing the user to the account inactive page. """ global _stash # noqa: F824 ret = getattr(_stash, "user", None) _stash.user = user return ret @classmethod def unstash_authenticated_user(cls): return cls._stash_user(None) ================================================ FILE: allauth/account/authentication.py ================================================ from allauth.account.internal.flows.login import AUTHENTICATION_METHODS_SESSION_KEY def get_authentication_records(request): return request.session.get(AUTHENTICATION_METHODS_SESSION_KEY, []) ================================================ FILE: allauth/account/checks.py ================================================ from django.core.checks import Critical, Warning, register @register() def adapter_check(app_configs, **kwargs): from allauth.account.adapter import get_adapter ret = [] adapter = get_adapter() if hasattr(adapter, "get_email_confirmation_redirect_url"): ret.append( Warning( msg="adapter.get_email_confirmation_redirect_url(request) is deprecated, use adapter.get_email_verification_redirect_url(email_address)" ) ) return ret @register() def settings_check(app_configs, **kwargs): from django.conf import settings from allauth import app_settings as allauth_app_settings from allauth.account import app_settings ret = [] if allauth_app_settings.SOCIALACCOUNT_ONLY: if app_settings.LOGIN_BY_CODE_ENABLED: ret.append( Critical( msg="SOCIALACCOUNT_ONLY does not work with ACCOUNT_LOGIN_BY_CODE_ENABLED" ) ) if allauth_app_settings.MFA_ENABLED: ret.append( Critical(msg="SOCIALACCOUNT_ONLY does not work with 'allauth.mfa'") ) if app_settings.EMAIL_VERIFICATION != app_settings.EmailVerificationMethod.NONE: ret.append( Critical( msg="SOCIALACCOUNT_ONLY requires ACCOUNT_EMAIL_VERIFICATION = 'none'" ) ) if ( app_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED and app_settings.EMAIL_VERIFICATION != app_settings.EmailVerificationMethod.MANDATORY ): ret.append( Critical( msg="ACCOUNT_EMAIL_VERFICATION_BY_CODE requires ACCOUNT_EMAIL_VERIFICATION = 'mandatory'" ) ) # Often made mistake: ACCOUNT_SIGNUP_FIELDS = [..., "password", ...] signup_fields = getattr(settings, "ACCOUNT_SIGNUP_FIELDS", None) for wrong_field, right_field in [ ("password", "password1"), ("password*", "password1*"), ]: if signup_fields and wrong_field in signup_fields: ret.append( Critical( msg=f"'{wrong_field}' is not a valid field for ACCOUNT_SIGNUP_FIELDS, use '{right_field}'", ) ) # Cross-check SIGNUP_FIELDS against LOGIN_METHODS. E.g. login is by email, email should be required signup_fields = app_settings.SIGNUP_FIELDS if not any( lm in signup_fields and signup_fields[lm]["required"] for lm in app_settings.LOGIN_METHODS ): ret.append( Warning( msg="ACCOUNT_LOGIN_METHODS conflicts with ACCOUNT_SIGNUP_FIELDS", id="account.W001", ) ) # If login includes email, email must be unique if ( app_settings.LoginMethod.EMAIL in app_settings.LOGIN_METHODS and not app_settings.UNIQUE_EMAIL ): ret.append( Critical(msg="Using email as a login method requires ACCOUNT_UNIQUE_EMAIL") ) # Mandatory email verification requires email email_required = "email" in signup_fields and signup_fields["email"]["required"] if ( app_settings.EMAIL_VERIFICATION == app_settings.EmailVerificationMethod.MANDATORY and not email_required ): ret.append( Critical( msg="ACCOUNT_EMAIL_VERIFICATION = 'mandatory' requires 'email*' in ACCOUNT_SIGNUP_FIELDS" ) ) if not app_settings.USER_MODEL_USERNAME_FIELD: if "username" in signup_fields: ret.append( Critical( msg="No ACCOUNT_USER_MODEL_USERNAME_FIELD, yet, ACCOUNT_SIGNUP_FIELDS contains 'username'" ) ) if app_settings.LoginMethod.USERNAME in app_settings.LOGIN_METHODS: ret.append( Critical( msg="No ACCOUNT_USER_MODEL_USERNAME_FIELD, yet, ACCOUNT_LOGIN_METHODS requires it" ) ) if ( app_settings.MAX_EMAIL_ADDRESSES is not None and app_settings.MAX_EMAIL_ADDRESSES <= 0 ): ret.append(Critical(msg="ACCOUNT_MAX_EMAIL_ADDRESSES must be None or > 0")) if app_settings.CHANGE_EMAIL: if ( app_settings.MAX_EMAIL_ADDRESSES is not None and app_settings.MAX_EMAIL_ADDRESSES != 2 ): ret.append( Critical( msg="Invalid combination of ACCOUNT_CHANGE_EMAIL and ACCOUNT_MAX_EMAIL_ADDRESSES" ) ) if hasattr(settings, "ACCOUNT_LOGIN_ATTEMPTS_LIMIT") or hasattr( settings, "ACCOUNT_LOGIN_ATTEMPTS_TIMEOUT" ): ret.append( Warning( msg="settings.ACCOUNT_LOGIN_ATTEMPTS_LIMIT/TIMEOUT is deprecated, use: settings.ACCOUNT_RATE_LIMITS['login_failed']" ) ) if hasattr(settings, "ACCOUNT_EMAIL_CONFIRMATION_COOLDOWN"): ret.append( Warning( msg="settings.ACCOUNT_EMAIL_CONFIRMATION_COOLDOWN is deprecated, use: settings.ACCOUNT_RATE_LIMITS['confirm_email']" ) ) if hasattr(settings, "ACCOUNT_AUTHENTICATION_METHOD"): converted = set(settings.ACCOUNT_AUTHENTICATION_METHOD.split("_")) ret.append( Warning( f"settings.ACCOUNT_AUTHENTICATION_METHOD is deprecated, use: settings.ACCOUNT_LOGIN_METHODS = {repr(converted)}" ) ) for field in [ "ACCOUNT_USERNAME_REQUIRED", "ACCOUNT_EMAIL_REQUIRED", "ACCOUNT_SIGNUP_EMAIL_ENTER_TWICE", "ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE", ]: if hasattr(settings, field): signup_fields_converted = [ k + ("*" if v["required"] else "") for k, v in app_settings.SIGNUP_FIELDS.items() ] ret.append( Warning( f"settings.{field} is deprecated, use: settings.ACCOUNT_SIGNUP_FIELDS = {repr(signup_fields_converted)}" ) ) if ( not allauth_app_settings.MFA_ENABLED and app_settings.LOGIN_BY_CODE_TRUST_ENABLED ): ret.append( Critical( msg="ACCOUNT_LOGIN_BY_CODE_TRUST_ENABLED requires MFA to be enabled" ) ) return ret ================================================ FILE: allauth/account/decorators.py ================================================ from functools import wraps from django.conf import settings from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth.decorators import login_required from django.core.exceptions import PermissionDenied from django.http import HttpResponseRedirect from django.shortcuts import render, resolve_url from django.urls import reverse from allauth.account import app_settings from allauth.account.internal.flows import reauthentication from allauth.account.internal.flows.email_verification import ( send_verification_email_for_user, ) from allauth.account.models import EmailAddress from allauth.account.utils import get_next_redirect_url from allauth.core.exceptions import ReauthenticationRequired from allauth.core.internal import httpkit def verified_email_required( function=None, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME ): """ Even when email verification is not mandatory during signup, there may be circumstances during which you really want to prevent unverified users to proceed. This decorator ensures the user is authenticated and has a verified email address. If the former is not the case then the behavior is identical to that of the standard `login_required` decorator. If the latter does not hold, email verification mails are automatically resend and the user is presented with a page informing them they needs to verify their email address. """ def decorator(view_func): @login_required(redirect_field_name=redirect_field_name, login_url=login_url) def _wrapped_view(request, *args, **kwargs): if not EmailAddress.objects.filter( user=request.user, verified=True ).exists(): send_verification_email_for_user(request, request.user) if app_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED: url = httpkit.add_query_params( reverse("account_email_verification_sent"), {REDIRECT_FIELD_NAME: request.get_full_path()}, ) return HttpResponseRedirect(url) return render(request, "account/verified_email_required.html") return view_func(request, *args, **kwargs) return _wrapped_view if function: return decorator(function) return decorator def reauthentication_required( function=None, redirect_field_name=REDIRECT_FIELD_NAME, allow_get=False, enabled=None, ): def decorator(view_func): @wraps(view_func) def _wrapper_view(request, *args, **kwargs): pass_method = allow_get and request.method == "GET" ena = (enabled is None) or ( enabled(request) if callable(enabled) else enabled ) if ena and not pass_method: if ( request.user.is_anonymous or not reauthentication.did_recently_authenticate(request) ): raise ReauthenticationRequired() return view_func(request, *args, **kwargs) return _wrapper_view if function: return decorator(function) return decorator def secure_admin_login(function=None): def decorator(view_func): @wraps(view_func) def _wrapper_view(request, *args, **kwargs): if request.user.is_authenticated: if not request.user.is_staff or not request.user.is_active: raise PermissionDenied() return view_func(request, *args, **kwargs) else: next_url = get_next_redirect_url(request) if not next_url: next_url = request.get_full_path() login_url = resolve_url(settings.LOGIN_URL) login_url = httpkit.add_query_params( login_url, {REDIRECT_FIELD_NAME: next_url} ) return HttpResponseRedirect(login_url) return _wrapper_view if function: return decorator(function) return decorator ================================================ FILE: allauth/account/fields.py ================================================ from django import forms from django.contrib.auth import password_validation from django.core.validators import RegexValidator from django.utils.translation import gettext_lazy as _ from allauth.account import app_settings from allauth.account.adapter import get_adapter class EmailField(forms.EmailField): def __init__(self, *args, **kwargs) -> None: kwargs.setdefault("label", _("Email")) kwargs.setdefault( "widget", forms.TextInput( attrs={ "type": "email", "autocomplete": "email", "placeholder": _("Email address"), } ), ) super().__init__(*args, **kwargs) def clean(self, value): return super().clean(value).lower() class PasswordField(forms.CharField): def __init__(self, *args, **kwargs): render_value = kwargs.pop( "render_value", app_settings.PASSWORD_INPUT_RENDER_VALUE ) kwargs["widget"] = forms.PasswordInput( render_value=render_value, attrs={"placeholder": kwargs.get("label")}, ) autocomplete = kwargs.pop("autocomplete", None) if autocomplete is not None: kwargs["widget"].attrs["autocomplete"] = autocomplete super().__init__(*args, **kwargs) class SetPasswordField(PasswordField): def __init__(self, *args, **kwargs): kwargs["autocomplete"] = "new-password" kwargs.setdefault( "help_text", password_validation.password_validators_help_text_html() ) super().__init__(*args, **kwargs) self.user = None def clean(self, value): value = super().clean(value) value = get_adapter().clean_password(value, user=self.user) return value class PhoneField(forms.CharField): e164_validator = RegexValidator( regex=r"^\+[1-9]\d{5,14}$", message=_("Enter a phone number including country code (e.g. +1 for the US)."), code="invalid_phone", ) def __init__(self, *args, **kwargs): widget = forms.TextInput( attrs={"placeholder": _("Phone"), "autocomplete": "tel", "type": "tel"} ) kwargs.setdefault("validators", [self.e164_validator]) kwargs.setdefault("widget", widget) kwargs.setdefault("label", _("Phone")) super().__init__(*args, **kwargs) def clean(self, value): value = super().clean(value) if value: value = value.replace(" ", "").replace("-", "") value = get_adapter().clean_phone(value) return value ================================================ FILE: allauth/account/forms.py ================================================ from django import forms from django.contrib.auth import REDIRECT_FIELD_NAME, get_user_model, password_validation from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.core import exceptions, validators from django.template.exceptions import TemplateDoesNotExist from django.template.loader import render_to_string from django.urls import NoReverseMatch, reverse from django.utils.safestring import mark_safe from django.utils.translation import gettext, gettext_lazy as _, pgettext from allauth.account.app_settings import LoginMethod from allauth.account.fields import EmailField, PasswordField, SetPasswordField from allauth.account.internal import flows from allauth.account.internal.flows.manage_email import ( email_already_exists, sync_user_email_address, ) from allauth.account.internal.flows.phone_verification import phone_already_exists from allauth.account.internal.flows.signup import base_signup_form_class from allauth.core import context, ratelimit from allauth.core.internal.cryptokit import compare_user_code from allauth.core.internal.httpkit import headed_redirect_response from allauth.utils import get_username_max_length, set_form_field_order from . import app_settings from .adapter import get_adapter from .models import EmailAddress, Login from .utils import ( filter_users_by_email, setup_user_email, url_str_to_user_pk, user_email, user_username, ) class EmailAwarePasswordResetTokenGenerator(PasswordResetTokenGenerator): def _make_hash_value(self, user, timestamp): ret = super()._make_hash_value(user, timestamp) sync_user_email_address(user) email = user_email(user) emails = set([email] if email else []) emails.update( EmailAddress.objects.filter(user=user).values_list("email", flat=True) ) ret += "|".join(sorted(emails)) return ret default_token_generator = app_settings.PASSWORD_RESET_TOKEN_GENERATOR() class PasswordVerificationMixin: def clean(self): cleaned_data = super().clean() password1 = cleaned_data.get("password1") password2 = cleaned_data.get("password2") if (password1 and password2) and password1 != password2: self.add_error("password2", _("You must type the same password each time.")) return cleaned_data class LoginForm(forms.Form): password = PasswordField(label=_("Password"), autocomplete="current-password") remember = forms.BooleanField(label=_("Remember Me"), required=False) user = None def __init__(self, *args, **kwargs) -> None: self.request = kwargs.pop("request", None) super().__init__(*args, **kwargs) adapter = get_adapter() login_field: forms.Field if app_settings.LOGIN_METHODS == {LoginMethod.EMAIL}: login_field = EmailField() elif app_settings.LOGIN_METHODS == {LoginMethod.USERNAME}: login_widget = forms.TextInput( attrs={"placeholder": _("Username"), "autocomplete": "username"} ) login_field = forms.CharField( label=_("Username"), widget=login_widget, max_length=get_username_max_length(), ) elif app_settings.LOGIN_METHODS == {LoginMethod.PHONE}: login_field = adapter.phone_form_field(required=True) else: login_widget = forms.TextInput( attrs={ "placeholder": self._get_login_field_placeholder(), "autocomplete": "email", } ) login_field = forms.CharField( label=pgettext("field label", "Login"), widget=login_widget ) self.fields["login"] = login_field set_form_field_order(self, ["login", "password", "remember"]) if app_settings.SESSION_REMEMBER is not None: del self.fields["remember"] self._setup_password_field() def _get_login_field_placeholder(self): methods = app_settings.LOGIN_METHODS assert len(methods) > 1 # nosec assert methods.issubset( { LoginMethod.USERNAME, LoginMethod.EMAIL, LoginMethod.PHONE, } ) # nosec if len(methods) == 3: placeholder = _("Username, email or phone") elif methods == {LoginMethod.USERNAME, LoginMethod.EMAIL}: placeholder = _("Username or email") elif methods == {LoginMethod.USERNAME, LoginMethod.PHONE}: placeholder = _("Username or phone") elif methods == {LoginMethod.EMAIL, LoginMethod.PHONE}: placeholder = _("Email or phone") else: raise ValueError(methods) return placeholder def _setup_password_field(self): password_field = app_settings.SIGNUP_FIELDS.get("password1") if not password_field: del self.fields["password"] return try: self.fields["password"].help_text = render_to_string( f"account/password_reset_help_text.{app_settings.TEMPLATE_EXTENSION}" ) return except TemplateDoesNotExist: pass try: reset_url = reverse("account_reset_password") except NoReverseMatch: pass else: forgot_txt = _("Forgot your password?") self.fields["password"].help_text = mark_safe( f'{forgot_txt}' ) # nosec def user_credentials(self) -> dict: """ Provides the credentials required to authenticate the user for login. """ login = self.cleaned_data["login"] method = flows.login.derive_login_method(login) credentials = {} credentials[method] = login # There are projects using usernames that look like email addresses, # yet, really are usernames. So, if username is a login method, always # give that a shot. if ( LoginMethod.USERNAME in app_settings.LOGIN_METHODS and method != LoginMethod.USERNAME ): credentials[LoginMethod.USERNAME] = login password = self.cleaned_data.get("password") if password: credentials["password"] = password return credentials def clean_login(self) -> str: login = self.cleaned_data["login"] return login.strip() def clean(self): cleaned_data = super().clean() if self._errors: return cleaned_data credentials = self.user_credentials() if "password" in credentials: return self._clean_with_password(credentials) return self._clean_without_password( credentials.get("email"), credentials.get("phone") ) def _clean_without_password(self, email: str | None, phone: str | None): """ If we don't have a password field, we need to replicate the request-login-code behavior. """ data = {} if email: data["email"] = email if phone: data["phone"] = phone if not data: self.add_error("login", get_adapter().validation_error("invalid_login")) else: form = RequestLoginCodeForm(data) if not form.is_valid(): for field in ["phone", "email"]: errors = form.errors.get(field) or [] # type: ignore for error in errors: self.add_error("login", error) else: self.user = form._user return self.cleaned_data def _clean_with_password(self, credentials: dict): adapter = get_adapter(self.request) user = adapter.authenticate(self.request, **credentials) if user: login = Login(user=user, email=credentials.get("email")) if flows.login.is_login_rate_limited(context.request, login): raise adapter.validation_error("too_many_login_attempts") self._login = login self.user = user else: login_method = flows.login.derive_login_method( login=self.cleaned_data["login"] ) raise adapter.validation_error(f"{login_method.value}_password_mismatch") return self.cleaned_data def login(self, request, redirect_url=None): credentials = self.user_credentials() if "password" in credentials: return self._login_with_password(request, redirect_url, credentials) return self._login_by_code(request, redirect_url, credentials) def _login_by_code(self, request, redirect_url, credentials): user = getattr(self, "user", None) phone = credentials.get("phone") email = credentials.get("email") flows.login_by_code.LoginCodeVerificationProcess.initiate( request=request, user=user, phone=phone, email=email, ) query = None if redirect_url: query = {} query[REDIRECT_FIELD_NAME] = redirect_url return headed_redirect_response("account_confirm_login_code", query=query) def _login_with_password(self, request, redirect_url, credentials): login = self._login login.redirect_url = redirect_url ret = flows.login.perform_password_login(request, credentials, login) remember = app_settings.SESSION_REMEMBER if remember is None: remember = self.cleaned_data["remember"] if remember: request.session.set_expiry(app_settings.SESSION_COOKIE_AGE) else: request.session.set_expiry(0) return ret class BaseSignupForm(base_signup_form_class()): # type: ignore[misc] username = forms.CharField( label=_("Username"), min_length=app_settings.USERNAME_MIN_LENGTH, widget=forms.TextInput( attrs={"placeholder": _("Username"), "autocomplete": "username"} ), ) email = EmailField() def __init__(self, *args, **kwargs) -> None: self._signup_fields = self._get_signup_fields(kwargs) self.account_already_exists = False super().__init__(*args, **kwargs) username_field = self.fields["username"] username_field.max_length = get_username_max_length() username_field.validators.append( validators.MaxLengthValidator(username_field.max_length) ) username_field.widget.attrs["maxlength"] = str(username_field.max_length) email2 = self._signup_fields.get("email2") if email2: self.fields["email2"] = EmailField( label=_("Email (again)"), required=email2["required"], widget=forms.TextInput( attrs={ "type": "email", "placeholder": _("Email address confirmation"), } ), ) email = self._signup_fields.get("email") if email: if email["required"]: self.fields["email"].label = gettext("Email") self.fields["email"].required = True else: self.fields["email"].label = gettext("Email (optional)") self.fields["email"].required = False self.fields["email"].widget.is_required = False else: del self.fields["email"] username = self._signup_fields.get("username") if username: if username["required"]: self.fields["username"].label = gettext("Username") self.fields["username"].required = True else: self.fields["username"].label = gettext("Username (optional)") self.fields["username"].required = False self.fields["username"].widget.is_required = False else: del self.fields["username"] phone = self._signup_fields.get("phone") self._has_phone_field = bool(phone) if phone: adapter = get_adapter() self.fields["phone"] = adapter.phone_form_field( label=_("Phone"), required=phone["required"] ) default_field_order = list(self._signup_fields.keys()) set_form_field_order( self, getattr(self, "field_order", None) or default_field_order ) def _get_signup_fields(self, kwargs): signup_fields = app_settings.SIGNUP_FIELDS if "email_required" in kwargs: email = signup_fields.get("email") if not email: raise exceptions.ImproperlyConfigured( "email required but not listed as a field" ) email["required"] = kwargs.pop("email_required") email2 = signup_fields.get("email2") if email2: email2["required"] = email["required"] if "username_required" in kwargs: username = signup_fields.get("username") if not username: raise exceptions.ImproperlyConfigured( "username required but not listed as a field" ) username["required"] = kwargs.pop("username_required") return signup_fields def clean_username(self) -> str: value = self.cleaned_data["username"] if not value and not self._signup_fields["username"]["required"]: return value value = get_adapter().clean_username(value) # Note regarding preventing enumeration: if the username is already # taken, but the email address is not, we would still leak information # if we were to send an email to that email address stating that the # username is already in use. return value def clean_email(self) -> str: value = self.cleaned_data["email"].lower() value = get_adapter().clean_email(value) if value: value = self.validate_unique_email(value) return value def clean_email2(self) -> str: value = self.cleaned_data["email2"].lower() return value def validate_unique_email(self, value) -> str: email, self.account_already_exists = flows.manage_email.email_already_exists( value ) return email def clean(self) -> dict: cleaned_data = super().clean() if "email2" in self._signup_fields: email = cleaned_data.get("email") email2 = cleaned_data.get("email2") if (email and email2) and email != email2: self.add_error("email2", _("You must type the same email each time.")) if "phone" in self._signup_fields: self._clean_phone() return cleaned_data def _clean_phone(self): """Intentionally NOT `clean_phone()`: - phone field is optional (depending on ACCOUNT_SIGNUP_FIELDS) - we don't want to have clean_phone() mistakenly called when a project is using a custom signup form with their own `phone` field. """ adapter = get_adapter() if phone := self.cleaned_data.get("phone"): user = adapter.get_user_by_phone(phone) if user: if not app_settings.PREVENT_ENUMERATION: self.add_error("phone", adapter.error_messages["phone_taken"]) else: self.account_already_exists = True def custom_signup(self, request, user) -> None: self.signup(request, user) def try_save(self, request): """Try and save the user. This can fail in case of a conflict on the email address, in that case we will send an "account already exists" email and return a standard "email verification sent" response. """ if self.account_already_exists: # Don't create a new account, only send an email informing the user # that (s)he already has one... email = self.cleaned_data.get("email") phone = None if "phone" in self._signup_fields: phone = self.cleaned_data.get("phone") resp = flows.signup.prevent_enumeration(request, email=email, phone=phone) user = None else: user = self.save(request) resp = None return user, resp def save(self, request): email = self.cleaned_data.get("email") if self.account_already_exists: raise ValueError(email) adapter = get_adapter() user = adapter.new_user(request) adapter.save_user(request, user, self) self.custom_signup(request, user) # TODO: Move into adapter `save_user` ? setup_user_email(request, user, [EmailAddress(email=email)] if email else []) return user class SignupForm(BaseSignupForm): def __init__(self, *args, **kwargs) -> None: self.by_passkey = kwargs.pop("by_passkey", False) super().__init__(*args, **kwargs) password1_field = self._signup_fields.get("password1") if not self.by_passkey and password1_field: self.fields["password1"] = PasswordField( label=_("Password"), autocomplete="new-password", help_text=password_validation.password_validators_help_text_html(), required=password1_field["required"], ) if "password2" in self._signup_fields: self.fields["password2"] = PasswordField( label=_("Password (again)"), autocomplete="new-password", required=password1_field["required"], ) if hasattr(self, "field_order"): set_form_field_order(self, self.field_order) honeypot_field_name = app_settings.SIGNUP_FORM_HONEYPOT_FIELD if honeypot_field_name: self.fields[honeypot_field_name] = forms.CharField( required=False, label="", widget=forms.TextInput( attrs={ "style": "position: absolute; right: -99999px;", "tabindex": "-1", "autocomplete": "nope", } ), ) def try_save(self, request): """ override of parent class method that adds additional catching of a potential bot filling out the honeypot field and returns a 'fake' email verification response if honeypot was filled out """ honeypot_field_name = app_settings.SIGNUP_FORM_HONEYPOT_FIELD if honeypot_field_name: if self.cleaned_data[honeypot_field_name]: user = None adapter = get_adapter() # honeypot fields work best when you do not report to the bot # that anything went wrong. So we return a fake email verification # sent response but without creating a user resp = adapter.respond_email_verification_sent(request, None) return user, resp return super().try_save(request) def clean(self) -> dict: super().clean() # `password` cannot be of type `SetPasswordField`, as we don't # have a `User` yet. So, let's populate a dummy user to be used # for password validation. User = get_user_model() dummy_user = User() user_username(dummy_user, self.cleaned_data.get("username")) user_email(dummy_user, self.cleaned_data.get("email")) password = self.cleaned_data.get("password1") if password: try: get_adapter().clean_password(password, user=dummy_user) except forms.ValidationError as e: self.add_error("password1", e) if ( "password2" in self._signup_fields and "password1" in self.cleaned_data and "password2" in self.cleaned_data ): if self.cleaned_data["password1"] != self.cleaned_data["password2"]: self.add_error( "password2", _("You must type the same password each time."), ) return self.cleaned_data class UserForm(forms.Form): def __init__(self, user=None, *args, **kwargs) -> None: self.user = user super().__init__(*args, **kwargs) class AddEmailForm(UserForm): email = EmailField(required=True) def clean_email(self) -> str: from allauth.account import signals value = self.cleaned_data["email"].lower() adapter = get_adapter() value = adapter.clean_email(value) users = filter_users_by_email(value) on_this_account = [u for u in users if u.pk == self.user.pk] on_diff_account = [u for u in users if u.pk != self.user.pk] if on_this_account: raise adapter.validation_error("duplicate_email") if ( # Email is taken by a different account... on_diff_account # We care about not having duplicate emails and app_settings.UNIQUE_EMAIL # Enumeration prevention is turned off. and (not app_settings.PREVENT_ENUMERATION) ): raise adapter.validation_error("email_taken") if not EmailAddress.objects.can_add_email(self.user): raise adapter.validation_error( "max_email_addresses", app_settings.MAX_EMAIL_ADDRESSES ) signals._add_email.send( sender=self.user.__class__, email=value, user=self.user, ) return value def save(self, request): if app_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED: email_address = EmailAddress( user=self.user, email=self.cleaned_data["email"] ) flows.email_verification.send_verification_email_to_address( request, email_address ) return email_address elif app_settings.CHANGE_EMAIL: return EmailAddress.objects.add_new_email( request, self.user, self.cleaned_data["email"] ) return EmailAddress.objects.add_email( request, self.user, self.cleaned_data["email"], confirm=True ) class ChangePasswordForm(PasswordVerificationMixin, UserForm): oldpassword = PasswordField( label=_("Current Password"), autocomplete="current-password" ) password1 = SetPasswordField(label=_("New Password")) password2 = PasswordField(label=_("New Password (again)")) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["password1"].user = self.user def clean_oldpassword(self) -> str: if not self.user.check_password(self.cleaned_data.get("oldpassword")): raise get_adapter().validation_error("enter_current_password") return self.cleaned_data["oldpassword"] def save(self) -> None: flows.password_change.change_password(self.user, self.cleaned_data["password1"]) class SetPasswordForm(PasswordVerificationMixin, UserForm): password1 = SetPasswordField(label=_("Password")) password2 = PasswordField(label=_("Password (again)")) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["password1"].user = self.user def save(self) -> None: flows.password_change.change_password(self.user, self.cleaned_data["password1"]) class ResetPasswordForm(forms.Form): email = EmailField(required=True) def clean_email(self) -> str: email = self.cleaned_data["email"].lower() email = get_adapter().clean_email(email) self.users = filter_users_by_email(email, is_active=True, prefer_verified=True) if not self.users and not app_settings.PREVENT_ENUMERATION: raise get_adapter().validation_error("unknown_email") return self.cleaned_data["email"] def save(self, request, **kwargs) -> str: email = self.cleaned_data["email"] if app_settings.PASSWORD_RESET_BY_CODE_ENABLED: flows.password_reset_by_code.PasswordResetVerificationProcess.initiate( request=request, user=(self.users[0] if self.users else None), email=email, ) else: token_generator = kwargs.get("token_generator", default_token_generator) flows.password_reset.request_password_reset( request, email, self.users, token_generator ) return email class ResetPasswordKeyForm(PasswordVerificationMixin, forms.Form): password1 = SetPasswordField(label=_("New Password")) password2 = PasswordField(label=_("New Password (again)")) def __init__(self, *args, **kwargs): self.user = kwargs.pop("user", None) self.temp_key = kwargs.pop("temp_key", None) super().__init__(*args, **kwargs) self.fields["password1"].user = self.user def save(self) -> None: flows.password_reset.reset_password(self.user, self.cleaned_data["password1"]) class UserTokenForm(forms.Form): uidb36 = forms.CharField() key = forms.CharField() reset_user = None token_generator = default_token_generator def _get_user(self, uidb36): User = get_user_model() try: pk = url_str_to_user_pk(uidb36) return User.objects.get(pk=pk) except (ValueError, User.DoesNotExist): return None def clean(self): cleaned_data = super().clean() uidb36 = cleaned_data.get("uidb36", None) key = cleaned_data.get("key", None) adapter = get_adapter() if not key: raise adapter.validation_error("invalid_password_reset") self.reset_user = self._get_user(uidb36) if self.reset_user is None or not self.token_generator.check_token( self.reset_user, key ): raise adapter.validation_error("invalid_password_reset") return cleaned_data class ReauthenticateForm(forms.Form): password = PasswordField(label=_("Password"), autocomplete="current-password") def __init__(self, *args, **kwargs) -> None: self.user = kwargs.pop("user") super().__init__(*args, **kwargs) def clean_password(self) -> str: password = self.cleaned_data["password"] if not get_adapter().reauthenticate(self.user, password): raise get_adapter().validation_error("incorrect_password") return password class RequestLoginCodeForm(forms.Form): email = EmailField() def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self._has_email = LoginMethod.EMAIL in app_settings.LOGIN_METHODS self._has_phone = LoginMethod.PHONE in app_settings.LOGIN_METHODS if self._has_phone: adapter = get_adapter() self.fields["phone"] = adapter.phone_form_field( required=not self._has_email ) self.fields["email"].required = False # Inconsistent, but kept for backwards compatibility: even if email is not a login # method the email field is added. May be used when login is by username. if self._has_phone and not self._has_email: self.fields.pop("email") def clean(self): cleaned_data = super().clean() adapter = get_adapter() phone = cleaned_data.get("phone") email = cleaned_data.get("email") if email and phone: raise adapter.validation_error("select_only_one") return cleaned_data def clean_phone(self) -> str: adapter = get_adapter() phone = self.cleaned_data["phone"] if phone: self._user = adapter.get_user_by_phone(phone) if not self._user and not app_settings.PREVENT_ENUMERATION: raise adapter.validation_error("unknown_phone") if not ratelimit.consume( context.request, action="request_login_code", key=phone.lower() ): raise adapter.validation_error("too_many_login_attempts") return phone def clean_email(self) -> str: adapter = get_adapter() email = self.cleaned_data["email"] if email: users = filter_users_by_email(email, is_active=True, prefer_verified=True) if not app_settings.PREVENT_ENUMERATION: if not users: raise adapter.validation_error("unknown_email") if not ratelimit.consume( context.request, action="request_login_code", key=email.lower() ): raise adapter.validation_error("too_many_login_attempts") self._user = users[0] if users else None return email class BaseConfirmCodeForm(forms.Form): code = forms.CharField( label=_("Code"), widget=forms.TextInput( attrs={"placeholder": _("Code"), "autocomplete": "one-time-code"}, ), ) def __init__(self, *args, **kwargs) -> None: self.expected_code = kwargs.pop("code", None) super().__init__(*args, **kwargs) def clean_code(self) -> str: code = self.cleaned_data["code"] if not compare_user_code(actual=code, expected=self.expected_code): raise get_adapter().validation_error("incorrect_code") return code class ConfirmLoginCodeForm(BaseConfirmCodeForm): pass class ConfirmEmailVerificationCodeForm(BaseConfirmCodeForm): def __init__(self, *args, **kwargs) -> None: self.user = kwargs.pop("user", None) self.email = kwargs.pop("email", None) super().__init__(*args, **kwargs) def clean_code(self) -> str: code = super().clean_code() if code: # We have a valid code. But, can we actually perform the change? email_already_exists(user=self.user, email=self.email, always_raise=True) return code class ConfirmPasswordResetCodeForm(BaseConfirmCodeForm): pass class VerifyPhoneForm(BaseConfirmCodeForm): def __init__(self, *args, **kwargs) -> None: self.user = kwargs.pop("user", None) self.phone = kwargs.pop("phone", None) super().__init__(*args, **kwargs) def clean_code(self) -> str: code = super().clean_code() if code: # We have a valid code. But, can we actually perform the change? phone_already_exists(self.user, self.phone, always_raise=True) return code class ChangePhoneForm(forms.Form): def __init__(self, *args, **kwargs) -> None: self.user = kwargs.pop("user", None) self.phone = kwargs.pop("phone", None) super().__init__(*args, **kwargs) adapter = get_adapter() self.fields["phone"] = adapter.phone_form_field(required=True) def clean_phone(self) -> str: phone = self.cleaned_data["phone"] adapter = get_adapter() if phone == self.phone: raise adapter.validation_error("same_as_current") self.account_already_exists = phone_already_exists(self.user, phone) return phone class ChangeEmailForm(forms.Form): email = EmailField(required=True) def __init__(self, *args, **kwargs) -> None: self.email = kwargs.pop("email", None) super().__init__(*args, **kwargs) def clean_email(self) -> str: email = self.cleaned_data["email"] if email == self.email: raise get_adapter().validation_error("same_as_current") email, self.account_already_exists = email_already_exists(email) return email ================================================ FILE: allauth/account/internal/__init__.py ================================================ from allauth.account.internal import flows __all__ = ["flows"] ================================================ FILE: allauth/account/internal/constants.py ================================================ from enum import Enum class LoginStageKey(str, Enum): LOGIN_BY_CODE = "login_by_code" VERIFY_EMAIL = "verify_email" VERIFY_PHONE = "verify_phone" ================================================ FILE: allauth/account/internal/decorators.py ================================================ from functools import wraps from django.contrib.auth import decorators from django.http import HttpResponseRedirect from django.urls import reverse from django.views.decorators.cache import never_cache from allauth.account.stages import LoginStageController from allauth.account.utils import get_login_redirect_url def _dummy_login_not_required(view_func): return view_func login_not_required = getattr( decorators, "login_not_required", _dummy_login_not_required ) def login_stage_required(stage: str, redirect_urlname: str): def decorator(view_func): @never_cache @login_not_required @wraps(view_func) def _wrapper_view(request, *args, **kwargs): if request.user.is_authenticated: return HttpResponseRedirect(get_login_redirect_url(request)) login_stage = LoginStageController.enter(request, stage) if not login_stage: return HttpResponseRedirect(reverse(redirect_urlname)) request._login_stage = login_stage return view_func(request, *args, **kwargs) return _wrapper_view return decorator ================================================ FILE: allauth/account/internal/emailkit.py ================================================ from django.core.exceptions import ValidationError from django.core.validators import validate_email from django.db.models.fields import EmailField from allauth.account.adapter import get_adapter def valid_email_or_none(email: str | None) -> str | None: ret: str | None = None try: if email: validate_email(email) max_length = EmailField().max_length if max_length is None or len(email) <= max_length: ret = get_adapter().clean_email(email.lower()) except ValidationError: pass return ret ================================================ FILE: allauth/account/internal/flows/__init__.py ================================================ from allauth.account.internal.flows import ( email_verification, email_verification_by_code, login, login_by_code, logout, manage_email, password_change, password_reset, password_reset_by_code, phone_verification, reauthentication, signup, ) __all__ = [ "email_verification", "email_verification_by_code", "login", "login_by_code", "logout", "manage_email", "password_change", "password_reset", "password_reset_by_code", "phone_verification", "reauthentication", "signup", ] ================================================ FILE: allauth/account/internal/flows/code_verification.py ================================================ import abc import time from typing import Any from django.contrib.auth import get_user_model from allauth.account.internal.userkit import str_to_user_id, user_id_to_str class AbstractCodeVerificationProcess(abc.ABC): def __init__( self, max_attempts: int, timeout: int, state: dict, user=None, ) -> None: self._user = user self.max_attempts = max_attempts self.timeout = timeout self.state = state @property def user(self): if self._user: return self._user user_id = self.state.get("user_id") if not user_id: return None user_id = str_to_user_id(user_id) self._user = get_user_model().objects.filter(pk=user_id).first() return self._user @property def code(self): return self.state.get("code", "") @classmethod def initial_state(cls, user, email: str | None = None, phone: str | None = None): state: dict[str, Any] = { "at": time.time(), "failed_attempts": 0, "resend_count": 0, "change_count": 0, } if email: state["email"] = email if phone: state["phone"] = phone if user: state["user_id"] = user_id_to_str(user) return state def record_invalid_attempt(self) -> bool: self.state["failed_attempts"] += 1 if self.state["failed_attempts"] >= self.max_attempts: self.abort() return False self.persist() return True def abort_if_invalid(self): if not self.is_valid(): self.abort() return None return self def is_valid(self) -> bool: return time.time() - self.state["at"] <= self.timeout @abc.abstractmethod def persist(self): ... # noqa: E704 @abc.abstractmethod def send(self): ... # noqa: E704 @abc.abstractmethod def abort(self): ... # noqa: E704 def is_resend_quota_reached(self, quota: int) -> bool: return self.state["resend_count"] >= quota def is_change_quota_reached(self, quota: int) -> bool: return self.state["change_count"] >= quota def record_change( self, *, email: str | None = None, phone: str | None = None ) -> None: self.state["change_count"] += 1 if email: self.state["email"] = email if phone: self.state["phone"] = phone def record_resend(self): self.state["resend_count"] += 1 @property def can_resend(self) -> bool: return False @property def can_change(self) -> bool: return False ================================================ FILE: allauth/account/internal/flows/email_verification.py ================================================ from django.contrib import messages from django.contrib.auth.base_user import AbstractBaseUser from django.http import HttpRequest, HttpResponse from django.urls import reverse from allauth.account import app_settings, signals from allauth.account.adapter import get_adapter from allauth.account.internal.flows.manage_email import ( emit_email_changed, sync_email_address, sync_user_email_address, ) from allauth.account.internal.flows.signup import send_unknown_account_mail from allauth.account.models import EmailAddress, Login from allauth.core.exceptions import ImmediateHttpResponse from allauth.core.internal import ratelimit from allauth.core.internal.httpkit import get_frontend_url from allauth.core.ratelimit import respond_429 from allauth.utils import build_absolute_uri def verify_email_indirectly( request: HttpRequest, user: AbstractBaseUser, email: str ) -> bool: try: email_address = EmailAddress.objects.get_for_user(user, email) except EmailAddress.DoesNotExist: return False else: if not email_address.verified: return verify_email(request, email_address) return True def verify_email_and_resume( request: HttpRequest, verification ) -> tuple[EmailAddress | None, HttpResponse | None]: email_address = verification.confirm(request) if not email_address: return None, None response = login_on_verification(request, email_address) return email_address, response def verify_email(request: HttpRequest, email_address: EmailAddress) -> bool: """ Marks the email address as confirmed on the db """ added = not email_address.pk from_email_address = ( EmailAddress.objects.filter(user_id=email_address.user_id) .exclude(pk=email_address.pk) .first() ) if not email_address.set_verified(commit=False): get_adapter(request).add_message( request, messages.ERROR, "account/messages/email_confirmation_failed.txt", {"email": email_address.email}, ) return False email_address.set_as_primary(conditional=(not app_settings.CHANGE_EMAIL)) email_address.save() if added: signals.email_added.send( sender=EmailAddress, request=request, user=request.user, email_address=email_address, ) signals.email_confirmed.send( sender=EmailAddress, request=request, email_address=email_address, ) if app_settings.CHANGE_EMAIL: for instance in EmailAddress.objects.filter( user_id=email_address.user_id ).exclude(pk=email_address.pk): instance.remove() emit_email_changed(request, from_email_address, email_address) get_adapter(request).add_message( request, messages.SUCCESS, "account/messages/email_confirmed.txt", {"email": email_address.email}, ) return True def get_email_verification_url(request: HttpRequest, emailconfirmation) -> str: """Constructs the email confirmation (activation) url. Note that if you have architected your system such that email confirmations are sent outside of the request context `request` can be `None` here. """ url = get_frontend_url(request, "account_confirm_email", key=emailconfirmation.key) if not url: url = reverse("account_confirm_email", args=[emailconfirmation.key]) url = build_absolute_uri(request, url) return url def login_on_verification(request, email_address) -> HttpResponse | None: """Simply logging in the user may become a security issue. If you do not take proper care (e.g. don't purge used email confirmations), a malicious person that got hold of the link will be able to login over and over again and the user is unable to do anything about it. Even restoring their own mailbox security will not help, as the links will still work. For password reset this is different, this mechanism works only as long as the attacker has access to the mailbox. If they no longer has access they cannot issue a password request and intercept it. Furthermore, all places where the links are listed (log files, but even Google Analytics) all of a sudden need to be secured. Purging the email confirmation once confirmed changes the behavior -- users will not be able to repeatedly confirm (in case they forgot that they already clicked the mail). All in all, we only login on verification when the user that is in the process of signing up is present in the session to avoid all of the above. This may not 100% work in case the user closes the browser (and the session gets lost), but at least we're secure. """ from allauth.account.stages import EmailVerificationStage, LoginStageController stage = LoginStageController.enter(request, EmailVerificationStage.key) if ( ( # Logging in on email verification is disabled... not app_settings.LOGIN_ON_EMAIL_CONFIRMATION # (but, that is only relevant for verification-by-link) and not app_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED ) or (request.user.is_authenticated) or (not stage or not stage.login.user) or (stage.login.user.pk != email_address.user_id) ): if stage: stage.abort() return None return stage.exit() def consume_email_verification_rate_limit( request: HttpRequest, email: str, dry_run: bool = False, raise_exception: bool = False, ) -> bool: return bool( ratelimit.consume( request, config=app_settings.RATE_LIMITS, action="confirm_email", key=email.lower(), dry_run=dry_run, raise_exception=raise_exception, limit_get=True, ) ) def handle_verification_email_rate_limit( request, email: str, raise_exception: bool = False ) -> bool: """ For email verification by link, it is not an issue if the user runs into rate limits. The reason is that the link is session independent. Therefore, if the user hits rate limits, we can just silently skip sending additional verification emails, as the previous emails that were already sent still contain valid links. This is different from email verification by code. Here, the session contains a specific code, meaning, silently skipping new verification emails is not an option, and we must hard fail (429) instead. The latter was missing, fixed. """ rl_ok = consume_email_verification_rate_limit( request, email, raise_exception=raise_exception ) if not rl_ok and app_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED: raise ImmediateHttpResponse(respond_429(request)) return rl_ok def get_address_for_user(user: AbstractBaseUser) -> EmailAddress | None: address = ( EmailAddress.objects.filter(user_id=user.pk) .order_by("-primary", "-verified") .first() ) if not address: address = sync_user_email_address(user) return address def get_address_for_login(login: Login): assert login.user # nosec if login.email: try: return EmailAddress.objects.get_for_user(login.user, login.email) except EmailAddress.DoesNotExist: # We do have an email, it's not stored as an EmailAddress. Might be the # case that a user was setup in the admin. So, let's sync # EmailAddress'es on the fly here. return sync_email_address(login.user, login.email) else: return get_address_for_user(login.user) def send_verification_email_for_user( request: HttpRequest, user: AbstractBaseUser ) -> bool: """ Used in the email-required-decorator. """ address = get_address_for_user(user) if not address: return False return send_verification_email_to_address(request, address) def send_verification_email_to_address( request: HttpRequest, address: EmailAddress, signup: bool = False, process=None, skip_enumeration_mails: bool = False, ) -> bool: """ Resend email verification for an existing email address. """ if app_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED: if not process: from allauth.account.internal.flows.email_verification_by_code import ( EmailVerificationProcess, ) process = EmailVerificationProcess.initiate( request=request, user=address.user if address.user_id else None, email=address.email, ) return process.did_send send = handle_verification_email_rate_limit( request, address.email, ) if not send: return False if not address.user_id: if skip_enumeration_mails: pass elif signup: get_adapter().send_account_already_exists_mail(address.email) else: send_unknown_account_mail(request, address.email) confirmation = None elif app_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED: get_adapter().send_confirmation_mail(request, process, signup=signup) confirmation = process else: confirmation = address.send_confirmation(request, signup=signup) add_email_verification_sent_message(request, address.email, signup=signup) if confirmation: signals.email_confirmation_sent.send( sender=confirmation.__class__, request=request, confirmation=confirmation, signup=signup, ) return True def send_verification_email_at_login(request: HttpRequest, login: Login) -> bool: """ At this point, it has already been confirmed that email verification is needed. Email verification mails are sent: a) Explicitly: when a user signs up b) Implicitly: when a user attempts to log in using an unverified email while EMAIL_VERIFICATION is mandatory. """ if not login.user: sent = send_verification_email_at_fake_login(request, login) else: sent = send_verification_email_at_real_login(request, login) return sent def send_verification_email_at_real_login(request: HttpRequest, login: Login) -> bool: assert login.user # nosec address = get_address_for_login(login) if not address: return False if address.verified: return False send = get_adapter().should_send_confirmation_mail(request, address, login.signup) if not send: return False return send_verification_email_to_address(request, address, signup=login.signup) def send_verification_email_at_fake_login(request: HttpRequest, login: Login) -> bool: """ Enumeration prevention. """ assert not login.user # nosec if not login.email: # Odd, no user & no email implies email enumeration prevention is # active, at signup, which implies we should have an email here? return False address = EmailAddress(user=None, email=login.email) return send_verification_email_to_address(request, address, signup=True) def add_email_verification_sent_message(request: HttpRequest, email: str, signup: bool): get_adapter().add_message( request, messages.INFO, "account/messages/email_confirmation_sent.txt", {"email": email, "login": not signup, "signup": signup}, ) def is_verification_rate_limited(request: HttpRequest, login: Login) -> bool: """ Returns whether or not the email verification is *hard* rate limited. Hard, meaning, it would be blocking login (verification by code, not link). """ if ( (not login.email) or (not app_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED) or login.email_verification != app_settings.EmailVerificationMethod.MANDATORY ): return False try: email_address = EmailAddress.objects.get_for_user(login.user, login.email) if not email_address.verified: if not consume_email_verification_rate_limit( request, login.email, dry_run=True ): return True except EmailAddress.DoesNotExist: pass return False def mark_email_address_as_verified( request: HttpRequest, address: EmailAddress ) -> EmailAddress | None: if not address.verified: confirmed = get_adapter().confirm_email(request, address) if confirmed: return address return None ================================================ FILE: allauth/account/internal/flows/email_verification_by_code.py ================================================ from typing import Optional from django.utils.functional import cached_property from allauth.account import app_settings from allauth.account.adapter import get_adapter from allauth.account.internal.flows.code_verification import ( AbstractCodeVerificationProcess, ) from allauth.account.internal.stagekit import clear_login from allauth.account.internal.userkit import did_user_login, user_email from allauth.account.models import EmailAddress from allauth.core import context EMAIL_VERIFICATION_CODE_SESSION_KEY = "account_email_verification_code" class EmailVerificationProcess(AbstractCodeVerificationProcess): def __init__(self, request, state: dict, user=None) -> None: self.request = request super().__init__( state=state, user=user, max_attempts=app_settings.EMAIL_VERIFICATION_BY_CODE_MAX_ATTEMPTS, timeout=app_settings.EMAIL_VERIFICATION_BY_CODE_TIMEOUT, ) @property def email(self) -> str: return self.state["email"] def persist(self) -> None: self.request.session[EMAIL_VERIFICATION_CODE_SESSION_KEY] = self.state def abort(self) -> None: self.request.session.pop(EMAIL_VERIFICATION_CODE_SESSION_KEY, None) clear_login(self.request) def send(self, skip_enumeration_mails: bool = False) -> None: from allauth.account.internal.flows.email_verification import ( send_verification_email_to_address, ) email_address = self.email_address signup = ( (not did_user_login(email_address.user)) if email_address.user_id else True ) self.did_send = send_verification_email_to_address( self.request, email_address, signup=signup, process=self, skip_enumeration_mails=skip_enumeration_mails, ) @cached_property def email_address(self) -> EmailAddress: email = self.state["email"] if not self.user or self.state.get("account_already_exists"): return EmailAddress(email=email) try: email_address = EmailAddress.objects.get_for_user(self.user, email) except EmailAddress.DoesNotExist: email_address = EmailAddress(user=self.user, email=email) return email_address def finish(self) -> EmailAddress | None: from allauth.account.internal.flows.email_verification import ( mark_email_address_as_verified, ) if not self.user or self.state.get("account_already_exists"): raise ValueError self.request.session.pop(EMAIL_VERIFICATION_CODE_SESSION_KEY, None) return mark_email_address_as_verified(self.request, self.email_address) def generate_code(self) -> None: self.state["code"] = get_adapter().generate_email_verification_code() @classmethod def initiate(cls, *, request, user, email: str) -> "EmailVerificationProcess": state = cls.initial_state(user, email) process = EmailVerificationProcess(request=request, user=user, state=state) process.generate_code() process.send() process.persist() return process @classmethod def resume(cls, request) -> Optional["EmailVerificationProcess"]: if not app_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED: return None state = request.session.get(EMAIL_VERIFICATION_CODE_SESSION_KEY) if not state: return None process = EmailVerificationProcess(request=request, state=state) return process.abort_if_invalid() @property def can_change(self) -> bool: # TODO: Prevent enumeration flaw: if we don't have a user, we cannot # change the email. To fix this, we would need to serialize # the user and perform an on-the-fly signup here. return ( not self.is_change_quota_reached( app_settings.EMAIL_VERIFICATION_MAX_CHANGE_COUNT ) and bool(self.user) and not did_user_login(self.user) ) def change_to(self, email: str, account_already_exists: bool) -> None: self.state["account_already_exists"] = account_already_exists self.generate_code() if account_already_exists: pass else: EmailAddress.objects.add_new_email( context.request, self.user, email, send_verification=False ) user_email(self.user, email, commit=True) self.record_change(email=email) self.send() self.persist() @property def can_resend(self) -> bool: return not self.is_resend_quota_reached( app_settings.EMAIL_VERIFICATION_MAX_RESEND_COUNT ) def resend(self) -> None: self.generate_code() self.send(skip_enumeration_mails=True) self.record_resend() self.persist() @property def key(self): return self.code ================================================ FILE: allauth/account/internal/flows/login.py ================================================ import time from typing import Any from django.core import exceptions, validators from django.http import HttpRequest, HttpResponse from allauth import app_settings as allauth_settings from allauth.account import app_settings from allauth.account.adapter import get_adapter from allauth.account.app_settings import LoginMethod from allauth.account.models import Login from allauth.account.signals import authentication_step_completed from allauth.core.exceptions import ImmediateHttpResponse AUTHENTICATION_METHODS_SESSION_KEY = "account_authentication_methods" def record_authentication(request, user, method: str, **extra_data) -> None: """Here we keep a log of all authentication methods used within the current session. Important to note is that having entries here does not imply that a user is fully signed in. For example, consider a case where a user authenticates using a password, but fails to complete the 2FA challenge. Or, a user successfully signs in into an inactive account or one that still needs verification. In such cases, ``request.user`` is still anonymous, yet, we do have an entry here. Example data:: {'method': 'password', 'at': 1701423602.7184925, 'username': 'john.doe'} {'method': 'socialaccount', 'at': 1701423567.6368647, 'provider': 'amazon', 'uid': 'amzn1.account.K2LI23KL2LK2'} {'method': 'mfa', 'at': 1701423602.6392953, 'id': 1, 'type': 'totp'} """ methods = request.session.get(AUTHENTICATION_METHODS_SESSION_KEY, []) data = { "method": method, "at": time.time(), } for k, v in extra_data.items(): if v is not None: data[k] = v methods.append(data) request.session[AUTHENTICATION_METHODS_SESSION_KEY] = methods authentication_step_completed.send( sender=user.__class__, request=request, method=method, user=user, **extra_data ) def _get_login_hook_kwargs(login: Login) -> dict[str, Any]: """ TODO: Just break backwards compatibility and pass only `login` to `pre/post_login()`. """ return dict( email_verification=login.email_verification, redirect_url=login.redirect_url, signal_kwargs=login.signal_kwargs, signup=login.signup, email=login.email, ) def perform_password_login( request: HttpRequest, credentials: dict[str, Any], login: Login ) -> HttpResponse: extra_data = { field: credentials.get(field) for field in ["email", "username"] if credentials.get(field) } record_authentication(request, login.user, method="password", **extra_data) return perform_login(request, login) def perform_login(request: HttpRequest, login: Login) -> HttpResponse: adapter = get_adapter() hook_kwargs = _get_login_hook_kwargs(login) if login.user: response = adapter.pre_login(request, login.user, **hook_kwargs) if response: return response return resume_login(request, login) def resume_login(request: HttpRequest, login: Login) -> HttpResponse: from allauth.account.stages import LoginStageController adapter = get_adapter() ctrl = LoginStageController(request, login) try: response = ctrl.handle() if response: return response adapter.login(request, login.user) hook_kwargs = _get_login_hook_kwargs(login) response = adapter.post_login(request, login.user, **hook_kwargs) if response: return response except ImmediateHttpResponse as e: if allauth_settings.HEADLESS_ENABLED: from allauth.headless.internal.restkit.response import APIResponse if isinstance(e.response, APIResponse): raise e response = e.response return response def is_login_rate_limited(request, login: Login) -> bool: from allauth.account.internal.flows.email_verification import ( is_verification_rate_limited, ) if is_verification_rate_limited(request, login): return True return False def derive_login_method(login: str) -> LoginMethod: if len(app_settings.LOGIN_METHODS) == 1: return next(iter(app_settings.LOGIN_METHODS)) if LoginMethod.EMAIL in app_settings.LOGIN_METHODS: try: validators.validate_email(login) return LoginMethod.EMAIL except exceptions.ValidationError: pass if LoginMethod.PHONE in app_settings.LOGIN_METHODS: try: get_adapter().phone_form_field(required=True).clean(login) return LoginMethod.PHONE except exceptions.ValidationError: pass return LoginMethod.USERNAME ================================================ FILE: allauth/account/internal/flows/login_by_code.py ================================================ from django.contrib import messages from allauth.account import app_settings from allauth.account.adapter import get_adapter from allauth.account.internal.flows.code_verification import ( AbstractCodeVerificationProcess, ) from allauth.account.internal.flows.email_verification import verify_email_indirectly from allauth.account.internal.flows.login import perform_login, record_authentication from allauth.account.internal.flows.phone_verification import verify_phone_indirectly from allauth.account.internal.flows.signup import send_unknown_account_mail from allauth.account.internal.stagekit import clear_login, stash_login from allauth.account.models import Login from allauth.account.stages import LoginByCodeStage, LoginStageController LOGIN_CODE_STATE_KEY = "login_code" class LoginCodeVerificationProcess(AbstractCodeVerificationProcess): def __init__(self, stage): self.stage = stage self.request = stage.request super().__init__( state=stage.state, timeout=app_settings.LOGIN_BY_CODE_TIMEOUT, max_attempts=app_settings.LOGIN_BY_CODE_MAX_ATTEMPTS, user=stage.login.user, ) def finish(self, redirect_url: str | None): email = self.state.get("email") phone = self.state.get("phone") user = self.user record_authentication( self.request, user, method="code", email=email, phone=phone ) if email: verify_email_indirectly(self.request, user, email) if phone: verify_phone_indirectly(self.request, user, phone) if self.state["initiated_by_user"]: # Just requesting a login code does is not considered to be a real login, # yet, is needed in order to make the stage machinery work. Now that we've # completed the code, let's start a real login. login = Login( user=user, redirect_url=redirect_url, email=email, ) return perform_login(self.request, login) else: return self.stage.exit() def abort(self) -> None: clear_login(self.request) def persist(self) -> None: stash_login(self.request, self.stage.login) def send(self, skip_enumeration_mails: bool = False) -> None: email = self.state.get("email") phone = self.state.get("phone") if email: self.send_by_email(email, skip_enumeration_mails=skip_enumeration_mails) elif phone: self.send_by_phone(phone, skip_enumeration_mails=skip_enumeration_mails) else: raise ValueError() def generate_code(self) -> str: email = self.state.get("email") phone = self.state.get("phone") adapter = get_adapter() if email: code = adapter.generate_login_code() elif phone: code = adapter._generate_phone_verification_code_compat( user=self.user, phone=phone ) else: raise ValueError() self.state["code"] = code return code def send_by_phone(self, phone: str, skip_enumeration_mails: bool = False) -> None: adapter = get_adapter() if self.user: code = self.generate_code() adapter.send_verification_code_sms(user=self.user, phone=phone, code=code) elif not skip_enumeration_mails: if self.stage.login.signup: adapter.send_account_already_exists_sms(phone) else: adapter.send_unknown_account_sms(phone) self.add_sent_message({"recipient": phone, "phone": phone}) def send_by_email(self, email: str, skip_enumeration_mails: bool = False) -> None: adapter = get_adapter() if self.user: code = self.generate_code() context = { "request": self.request, "code": code, } adapter.send_mail("account/email/login_code", email, context) elif not skip_enumeration_mails: if self.stage.login.signup: adapter.send_account_already_exists_mail(email) else: send_unknown_account_mail(self.request, email) self.add_sent_message({"email": email, "recipient": email}) def add_sent_message(self, context) -> None: get_adapter().add_message( self.request, messages.SUCCESS, "account/messages/login_code_sent.txt", context, ) @classmethod def initiate( cls, *, request, user, email: str | None = None, phone: str | None = None, stage=None, ): initial_state = cls.initial_state(user=user, email=email, phone=phone) initial_state["initiated_by_user"] = stage is None if not stage: login = Login(user=user, email=email) login.state["stages"] = {"current": "login_by_code"} stage = LoginByCodeStage( LoginStageController(request, login), request, login ) stage.state.update(initial_state) process = LoginCodeVerificationProcess(stage=stage) process.send() process.persist() return process @classmethod def resume(cls, stage): process = LoginCodeVerificationProcess(stage=stage) return process.abort_if_invalid() @property def can_resend(self) -> bool: return not self.is_resend_quota_reached( app_settings.LOGIN_BY_CODE_MAX_RESEND_COUNT ) def resend(self) -> None: self.generate_code() self.send(skip_enumeration_mails=True) self.record_resend() self.persist() ================================================ FILE: allauth/account/internal/flows/logout.py ================================================ from django.contrib import messages from django.http import HttpRequest from allauth.account.internal.flows.password_reset_by_code import ( PASSWORD_RESET_VERIFICATION_SESSION_KEY, ) from allauth.account.internal.stagekit import clear_login def logout(request: HttpRequest, *, show_message: bool = True) -> None: from allauth.account.adapter import get_adapter from allauth.account.views import INTERNAL_RESET_SESSION_KEY if request.user.is_authenticated: adapter = get_adapter() if show_message: adapter.add_message( request, messages.SUCCESS, "account/messages/logged_out.txt" ) adapter.logout(request) clear_login(request) request.session.pop(PASSWORD_RESET_VERIFICATION_SESSION_KEY, None) request.session.pop(INTERNAL_RESET_SESSION_KEY, None) ================================================ FILE: allauth/account/internal/flows/manage_email.py ================================================ from django.contrib import messages from django.contrib.auth.base_user import AbstractBaseUser from django.http import HttpRequest from allauth.account import app_settings, signals from allauth.account.adapter import get_adapter from allauth.account.internal.flows.reauthentication import ( raise_if_reauthentication_required, ) from allauth.account.internal.userkit import user_email from allauth.account.models import EmailAddress def can_delete_email(email_address: EmailAddress) -> bool: adapter = get_adapter() return adapter.can_delete_email(email_address) def delete_email(request: HttpRequest, email_address: EmailAddress) -> bool: if app_settings.REAUTHENTICATION_REQUIRED: raise_if_reauthentication_required(request) success = False adapter = get_adapter() if not can_delete_email(email_address): adapter.add_message( request, messages.ERROR, "account/messages/cannot_delete_primary_email.txt", {"email": email_address.email}, ) else: email_address.remove() signals.email_removed.send( sender=EmailAddress, request=request, user=request.user, email_address=email_address, ) adapter.add_message( request, messages.SUCCESS, "account/messages/email_deleted.txt", {"email": email_address.email}, ) adapter.send_notification_mail( "account/email/email_deleted", request.user, {"deleted_email": email_address.email}, ) success = True return success def add_email(request: HttpRequest, form) -> None: if app_settings.REAUTHENTICATION_REQUIRED: raise_if_reauthentication_required(request) email_address = form.save(request) if email_address.pk: signals.email_added.send( sender=EmailAddress, request=request, user=request.user, email_address=email_address, ) def can_mark_as_primary(email_address: EmailAddress) -> bool: if not email_address.pk: return False return ( email_address.verified or not EmailAddress.objects.filter( user=email_address.user, verified=True ).exists() ) def mark_as_primary(request: HttpRequest, email_address: EmailAddress) -> bool: if app_settings.REAUTHENTICATION_REQUIRED: raise_if_reauthentication_required(request) # Not primary=True -- Slightly different variation, don't # require verified unless moving from a verified # address. Ignore constraint if previous primary email # address is not verified. success = False if not can_mark_as_primary(email_address): get_adapter().add_message( request, messages.ERROR, "account/messages/unverified_primary_email.txt", ) else: assert request.user.is_authenticated # nosec from_email_address = EmailAddress.objects.filter( user=request.user, primary=True ).first() email_address.set_as_primary() adapter = get_adapter() adapter.add_message( request, messages.SUCCESS, "account/messages/primary_email_set.txt", ) emit_email_changed(request, from_email_address, email_address) success = True return success def emit_email_changed( request: HttpRequest, from_email_address: EmailAddress | None, to_email_address: EmailAddress, ) -> None: user = to_email_address.user signals.email_changed.send( sender=EmailAddress, request=request, user=user, from_email_address=from_email_address, to_email_address=to_email_address, ) if from_email_address: get_adapter().send_notification_mail( "account/email/email_changed", user, context={ "from_email": from_email_address.email, "to_email": to_email_address.email, }, email=from_email_address.email, ) def assess_unique_email( email: str, user: AbstractBaseUser | None = None ) -> bool | None: """ True -- email is unique False -- email is already in use None -- email is in use, but we should hide that using email verification. """ from allauth.account.utils import filter_users_by_email if not app_settings.UNIQUE_EMAIL: return True users_with_email = filter_users_by_email(email) if user: users_with_email = [u for u in users_with_email if u.pk != user.pk] conflict = len(users_with_email) > 0 if not conflict: # All good. return True elif not app_settings.PREVENT_ENUMERATION: # Fail right away. return False elif ( app_settings.EMAIL_VERIFICATION == app_settings.EmailVerificationMethod.MANDATORY ): # In case of mandatory verification and enumeration prevention, # we can avoid creating a new account with the same (unverified) # email address, because we are going to send an email anyway. assert app_settings.PREVENT_ENUMERATION # nosec return None elif app_settings.PREVENT_ENUMERATION == "strict": # We're going to be strict on enumeration prevention, and allow for # this email address to pass even though it already exists. In this # scenario, you can signup multiple times using the same email # address resulting in multiple accounts with an unverified email. return True else: assert app_settings.PREVENT_ENUMERATION is True # nosec # Conflict. We're supposed to prevent enumeration, but we can't # because that means letting the user in, while emails are required # to be unique. In this case, uniqueness takes precedence over # enumeration prevention. return False def list_email_addresses( request: HttpRequest, user: AbstractBaseUser ) -> list[EmailAddress]: addresses = list(EmailAddress.objects.filter(user_id=user.pk)) if ( app_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED and request.user.is_authenticated ): from allauth.account.internal.flows.email_verification_by_code import ( EmailVerificationProcess, ) process = EmailVerificationProcess.resume(request) if process: email_address = process.email_address if email_address.user_id == user.pk: addresses.append(email_address) return addresses def email_already_exists( email: str, user: AbstractBaseUser | None = None, always_raise: bool = False ) -> tuple[str, bool]: """ Throws a validation error (if allowed by enumeration prevention rules). Returns a tuple of [email, already_exists]. """ adapter = get_adapter() assessment = assess_unique_email(email, user=user) if assessment is True: # No conflict already_exists = False elif assessment is False: # Fail right away. raise adapter.validation_error("email_taken") else: assert assessment is None # nosec already_exists = True email = adapter.validate_unique_email(email) if already_exists and always_raise: raise adapter.validation_error("email_taken") return (email, already_exists) def sync_user_email_address(user: AbstractBaseUser) -> EmailAddress | None: """ Keep user.email in sync with user.emailaddress_set. Under some circumstances the user.email may not have ended up as an EmailAddress record, e.g. in the case of manually created admin users. """ email = user_email(user) if email: return sync_email_address(user, email) return None def sync_email_address(user: AbstractBaseUser, email: str) -> EmailAddress | None: if not EmailAddress.objects.filter(user_id=user.pk, email=email).exists(): # get_or_create() to gracefully handle races address, _ = EmailAddress.objects.get_or_create( user=user, email=email, defaults={"primary": False, "verified": False} ) else: address = None return address ================================================ FILE: allauth/account/internal/flows/password_change.py ================================================ from django.contrib import messages from django.contrib.auth import update_session_auth_hash from django.contrib.auth.models import AbstractBaseUser from django.http import HttpRequest from allauth.account import app_settings, signals from allauth.account.adapter import get_adapter from allauth.account.internal.flows.logout import logout def change_password(user: AbstractBaseUser, password: str) -> None: get_adapter().set_password(user, password) def finalize_password_change(request: HttpRequest, user: AbstractBaseUser) -> bool: logged_out = logout_on_password_change(request, user) adapter = get_adapter(request) adapter.add_message( request, messages.SUCCESS, "account/messages/password_changed.txt", ) adapter.send_notification_mail("account/email/password_changed", user) signals.password_changed.send( sender=user.__class__, request=request, user=user, ) return logged_out def finalize_password_set(request: HttpRequest, user: AbstractBaseUser) -> bool: logged_out = logout_on_password_change(request, user) adapter = get_adapter(request) adapter.add_message(request, messages.SUCCESS, "account/messages/password_set.txt") adapter.send_notification_mail("account/email/password_set", user) signals.password_set.send( sender=user.__class__, request=request, user=user, ) return logged_out def logout_on_password_change(request: HttpRequest, user: AbstractBaseUser) -> bool: # Since it is the default behavior of Django to invalidate all sessions on # password change, this function actually has to preserve the session when # logout isn't desired. logged_out = True if not app_settings.LOGOUT_ON_PASSWORD_CHANGE: update_session_auth_hash(request, user) # type: ignore[arg-type] logged_out = False else: logout(request) return logged_out ================================================ FILE: allauth/account/internal/flows/password_reset.py ================================================ from urllib.parse import quote from django.contrib import messages from django.contrib.auth.models import AbstractBaseUser from django.http import HttpRequest, HttpResponse from django.urls import reverse from allauth.account import app_settings, signals from allauth.account.adapter import get_adapter from allauth.account.app_settings import LoginMethod from allauth.account.internal.flows.login import perform_login, record_authentication from allauth.account.internal.flows.signup import send_unknown_account_mail from allauth.account.models import EmailAddress, Login from allauth.core.internal.httpkit import get_frontend_url from allauth.utils import build_absolute_uri def reset_password(user: AbstractBaseUser, password: str) -> None: get_adapter().set_password(user, password) def perform_password_reset_login( request: HttpRequest, user: AbstractBaseUser, phone: str | None = None, email: str | None = None, ) -> HttpResponse: extra_data = {} if phone: extra_data["phone"] = phone elif email: extra_data["email"] = email record_authentication(request, user, method="password_reset", **extra_data) login = Login(user=user, email=email) return perform_login(request, login) def finalize_password_reset( request: HttpRequest, user: AbstractBaseUser, email: str | None = None ) -> HttpResponse | None: adapter = get_adapter() if user: # User successfully reset the password, clear any # possible cache entries for all email addresses. for address in EmailAddress.objects.filter(user_id=user.pk): adapter._delete_login_attempts_cached_email(request, email=address.email) adapter.add_message( request, messages.SUCCESS, "account/messages/password_changed.txt", ) signals.password_reset.send( sender=user.__class__, request=request, user=user, ) adapter.send_notification_mail("account/email/password_reset", user) if app_settings.LOGIN_ON_PASSWORD_RESET: return perform_password_reset_login(request, user, email=email) return None def get_reset_password_url(request: HttpRequest) -> str: url = get_frontend_url(request, "account_reset_password") if not url: url = build_absolute_uri(request, reverse("account_reset_password")) return url def get_reset_password_from_key_url(request: HttpRequest, key: str) -> str: """ Method intented to be overriden in case the password reset email needs to point to your frontend/SPA. """ url = get_frontend_url(request, "account_reset_password_from_key", key=key) if not url: # We intentionally accept an opaque `key` on the interface here, and not # implementation details such as a separate `uidb36` and `key. Ideally, # this should have done on `urls` level as well. path = reverse( "account_reset_password_from_key", kwargs={"uidb36": "UID", "key": "KEY"} ) path = path.replace("UID-KEY", quote(key)) url = build_absolute_uri(request, path) return url def request_password_reset(request, email: str, users, token_generator) -> None: from allauth.account.utils import user_pk_to_url_str, user_username if not users: send_unknown_account_mail(request, email) return adapter = get_adapter() for user in users: temp_key = ( token_generator or app_settings.PASSWORD_RESET_TOKEN_GENERATOR() ).make_token(user) # send the password reset email uid = user_pk_to_url_str(user) # We intentionally pass an opaque `key` on the interface here, and # not implementation details such as a separate `uidb36` and # `key. Ideally, this should have done on `urls` level as well. key = f"{uid}-{temp_key}" url = adapter.get_reset_password_from_key_url(key) context = { "user": user, "password_reset_url": url, "uid": uid, "key": temp_key, "request": request, } if LoginMethod.USERNAME in app_settings.LOGIN_METHODS: context["username"] = user_username(user) adapter.send_password_reset_mail(user, email, context) ================================================ FILE: allauth/account/internal/flows/password_reset_by_code.py ================================================ from typing import Optional from django.http import HttpRequest, HttpResponse from allauth.account import app_settings from allauth.account.adapter import get_adapter from allauth.account.internal.flows import password_reset from allauth.account.internal.flows.code_verification import ( AbstractCodeVerificationProcess, ) from allauth.account.internal.flows.email_verification import verify_email_indirectly from allauth.account.internal.flows.signup import send_unknown_account_mail PASSWORD_RESET_VERIFICATION_SESSION_KEY = ( "account_password_reset_verification" # nosec: B105 ) class PasswordResetVerificationProcess(AbstractCodeVerificationProcess): def __init__(self, request, state, user=None): self.request = request super().__init__( state=state, timeout=app_settings.PASSWORD_RESET_BY_CODE_TIMEOUT, max_attempts=app_settings.PASSWORD_RESET_BY_CODE_MAX_ATTEMPTS, user=user, ) def abort(self): self.request.session.pop(PASSWORD_RESET_VERIFICATION_SESSION_KEY, None) def confirm_code(self): if self.state.get("code_confirmed"): return self.state["code_confirmed"] = True self.persist() verify_email_indirectly(self.request, self.user, self.state["email"]) def finish(self) -> HttpResponse | None: self.request.session.pop(PASSWORD_RESET_VERIFICATION_SESSION_KEY, None) return password_reset.finalize_password_reset( self.request, self.user, email=self.state["email"] ) def persist(self): self.request.session[PASSWORD_RESET_VERIFICATION_SESSION_KEY] = self.state def send(self): adapter = get_adapter() email = self.state["email"] if not self.user: send_unknown_account_mail(self.request, email) return code = adapter.generate_password_reset_code() self.state["code"] = code context = { "request": self.request, "code": self.code, } adapter.send_mail("account/email/password_reset_code", email, context) @classmethod def initiate(cls, *, request, user, email: str): state = cls.initial_state(user, email) process = PasswordResetVerificationProcess(request, state=state, user=user) process.send() process.persist() return process @classmethod def resume( cls, request: HttpRequest ) -> Optional["PasswordResetVerificationProcess"]: state = request.session.get(PASSWORD_RESET_VERIFICATION_SESSION_KEY) if not state: return None process = PasswordResetVerificationProcess(request, state=state) return process.abort_if_invalid() ================================================ FILE: allauth/account/internal/flows/phone_verification.py ================================================ from typing import Optional from django.contrib import messages from django.http import HttpRequest from allauth.account import app_settings from allauth.account.adapter import get_adapter from allauth.account.internal.flows.code_verification import ( AbstractCodeVerificationProcess, ) from allauth.account.internal.flows.reauthentication import ( raise_if_reauthentication_required, ) from allauth.account.internal.stagekit import stash_login from allauth.account.internal.userkit import did_user_login from allauth.core import context, ratelimit PHONE_VERIFICATION_STATE_KEY = "phone_verification" PHONE_VERIFICATION_SESSION_KEY = "account_phone_verification" def verify_phone_indirectly(request: HttpRequest, user, phone: str) -> None: get_adapter().set_phone_verified(user, phone) class PhoneVerificationProcess(AbstractCodeVerificationProcess): def __init__(self, user, state): super().__init__( user=user, state=state, timeout=app_settings.PHONE_VERIFICATION_TIMEOUT, max_attempts=app_settings.PHONE_VERIFICATION_MAX_ATTEMPTS, ) @property def phone(self) -> str: return self.state["phone"] def send(self, skip_enumeration_sms: bool = False) -> None: ratelimit.consume( context.request, action="verify_phone", key=self.phone, raise_exception=True, ) adapter = get_adapter() if self.user: code = adapter._generate_phone_verification_code_compat( user=self.user, phone=self.phone, ) else: code = "" self.state["code"] = code self.send_sms(skip_enumeration_sms) get_adapter().add_message( context.request, messages.INFO, "account/messages/phone_verification_sent.txt", {"phone": self.phone}, ) self.persist() def send_sms(self, skip_enumeration_sms: bool) -> None: adapter = get_adapter() if not self.user or self.state.get("account_already_exists"): if not skip_enumeration_sms: if self.state.get("account_already_exists") or self.state.get("signup"): adapter.send_account_already_exists_sms(self.phone) else: adapter.send_unknown_account_sms(self.phone) return adapter.send_verification_code_sms( user=self.user, code=self.code, phone=self.phone, ) def finish(self) -> None: phone = self.state["phone"] adapter = get_adapter() adapter.set_phone_verified(self.user, phone) adapter.add_message( context.request, messages.SUCCESS, "account/messages/phone_verified.txt", {"phone": phone}, ) class PhoneVerificationStageProcess(PhoneVerificationProcess): def __init__(self, stage): self.stage = stage super().__init__(user=stage.login.user, state=stage.state) def abort(self): pass def persist(self): stash_login(self.stage.request, self.stage.login) @classmethod def initiate(cls, *, stage, phone: str): stage.state.update(cls.initial_state(user=stage.login.user, phone=phone)) process = PhoneVerificationStageProcess(stage=stage) process.state["signup"] = stage.login.signup process.send(skip_enumeration_sms=False) return process @classmethod def resume(cls, stage) -> Optional["PhoneVerificationStageProcess"]: state = stage.state if not state: return None process = PhoneVerificationStageProcess(stage) return process.abort_if_invalid() def change_to(self, phone: str, account_already_exists: bool) -> None: self.state["account_already_exists"] = account_already_exists self.record_change(phone=phone) adapter = get_adapter() if not account_already_exists: adapter.set_phone(self.user, phone, False) self.send(skip_enumeration_sms=False) self.persist() def resend(self): self.record_resend() self.send(skip_enumeration_sms=True) @property def can_resend(self) -> bool: return not self.is_resend_quota_reached( app_settings.PHONE_VERIFICATION_MAX_RESEND_COUNT ) @property def can_change(self) -> bool: # TODO: Prevent enumeration flaw: if we don't have a user, we cannot # change the phone. To fix this, we would need to serialize # the user and perform an on-the-fly signup here. return ( not self.is_change_quota_reached( app_settings.PHONE_VERIFICATION_MAX_CHANGE_COUNT ) and bool(self.user) and not did_user_login(self.user) ) class ChangePhoneVerificationProcess(PhoneVerificationProcess): def __init__(self, request: HttpRequest, state: dict): self.request = request super().__init__( user=request.user, state=state, ) def abort(self): self.request.session.pop(PHONE_VERIFICATION_SESSION_KEY, None) def persist(self): self.request.session[PHONE_VERIFICATION_SESSION_KEY] = self.state def finish(self): super().finish() self.request.session.pop(PHONE_VERIFICATION_SESSION_KEY, None) @classmethod def initiate(cls, request: HttpRequest, phone: str): if app_settings.REAUTHENTICATION_REQUIRED: raise_if_reauthentication_required(request) state = cls.initial_state(user=request.user, phone=phone) process = ChangePhoneVerificationProcess(request, state=state) process.send() return process @classmethod def resume(cls, request: HttpRequest) -> Optional["ChangePhoneVerificationProcess"]: state = request.session.get(PHONE_VERIFICATION_SESSION_KEY) if not state: return None process = ChangePhoneVerificationProcess(request, state=state) return process.abort_if_invalid() def phone_already_exists(user, phone: str, always_raise: bool = False) -> bool: """ Throws a validation error (if allowed by enumeration prevention rules). Otherwise, returns True iff another account already exists. """ adapter = get_adapter() other_user = adapter.get_user_by_phone(phone) already_exists = other_user and (not user or user.pk != other_user.pk) if already_exists and (not app_settings.PREVENT_ENUMERATION or always_raise): raise adapter.validation_error("phone_taken") return already_exists ================================================ FILE: allauth/account/internal/flows/reauthentication.py ================================================ import time from django.contrib.auth import REDIRECT_FIELD_NAME from django.http import HttpRequest, HttpResponseRedirect from django.urls import reverse from django.utils.http import urlencode from allauth import app_settings as allauth_settings from allauth.account import app_settings from allauth.account.authentication import get_authentication_records from allauth.account.internal.flows.login import record_authentication from allauth.core.exceptions import ReauthenticationRequired from allauth.core.internal.httpkit import deserialize_request, serialize_request from allauth.core.internal.urlkit import script_aware_resolve from allauth.utils import import_callable STATE_SESSION_KEY = "account_reauthentication_state" def reauthenticate_by_password(request: HttpRequest) -> None: record_authentication( request, request.user, method="password", reauthenticated=True ) def stash_and_reauthenticate( request: HttpRequest, state: dict, callback: str ) -> HttpResponseRedirect: request.session[STATE_SESSION_KEY] = { "state": state, "callback": callback, } return HttpResponseRedirect(reverse("account_reauthenticate")) def suspend_request(request: HttpRequest, redirect_to: str) -> HttpResponseRedirect: path = request.get_full_path() if request.method == "POST": request.session[STATE_SESSION_KEY] = {"request": serialize_request(request)} return HttpResponseRedirect( f"{redirect_to}?{urlencode({REDIRECT_FIELD_NAME: path})}" ) def resume_request(request: HttpRequest) -> HttpResponseRedirect | None: from allauth.account.utils import get_next_redirect_url state = request.session.pop(STATE_SESSION_KEY, None) if state and "callback" in state: callback = import_callable(state["callback"]) return callback(request, state["state"]) url = get_next_redirect_url(request, REDIRECT_FIELD_NAME) if not url: return None if state and "request" in state: suspended_request = deserialize_request(state["request"], request) if suspended_request.path == url: resolved = script_aware_resolve(suspended_request.path) return resolved.func(suspended_request, *resolved.args, **resolved.kwargs) return HttpResponseRedirect(url) def raise_if_reauthentication_required(request: HttpRequest) -> None: if not did_recently_authenticate(request): raise ReauthenticationRequired() def did_recently_authenticate(request: HttpRequest) -> bool: if request.user.is_anonymous: return False if not get_reauthentication_flows(request.user): # TODO: This user only has social accounts attached. Now, ideally, you # would want to reauthenticate over at the social account provider. For # now, this is not implemented. Although definitely suboptimal, this # method is currently used for reauthentication checks over at MFA, and, # users that delegate the security of their account to an external # provider like Google typically use MFA over there anyway. return True methods = get_authentication_records(request) if not methods: return False authenticated_at = methods[-1]["at"] return time.time() - authenticated_at < app_settings.REAUTHENTICATION_TIMEOUT def get_reauthentication_flows(user) -> list[dict]: ret: list[dict] = [] if not user.is_authenticated: return ret if user.has_usable_password(): entry = { "id": "reauthenticate", } ret.append(entry) if allauth_settings.MFA_ENABLED: from allauth.mfa.models import Authenticator from allauth.mfa.utils import is_mfa_enabled types = [] for typ in Authenticator.Type: if is_mfa_enabled(user, types=[typ]): types.append(typ) if types: ret.append({"id": "mfa_reauthenticate", "types": types}) return ret ================================================ FILE: allauth/account/internal/flows/signup.py ================================================ from importlib import import_module from django import forms from django.core import exceptions from django.http import HttpRequest, HttpResponse from django.urls import reverse from allauth.account import app_settings, signals from allauth.account.adapter import get_adapter from allauth.account.internal.flows.login import perform_login from allauth.account.models import Login from allauth.core import context from allauth.core.internal.httpkit import get_frontend_url from allauth.utils import build_absolute_uri class DummyCustomSignupForm(forms.Form): def signup(self, request, user) -> None: """ Invoked at signup time to complete the signup of the user. """ pass def base_signup_form_class(): """ Currently, we inherit from the custom form, if any. This is all not very elegant, though it serves a purpose: - There are two signup forms: one for local accounts, and one for social accounts - Both share a common base (BaseSignupForm) - Given the above, how to put in a custom signup form? Which form would your custom form derive from, the local or the social one? """ if not app_settings.SIGNUP_FORM_CLASS: return DummyCustomSignupForm try: fc_module, fc_classname = app_settings.SIGNUP_FORM_CLASS.rsplit(".", 1) except ValueError: raise exceptions.ImproperlyConfigured( f"{app_settings.SIGNUP_FORM_CLASS} does not point to a form class" ) try: mod = import_module(fc_module) except ImportError as e: raise exceptions.ImproperlyConfigured( f'Error importing form class {fc_module}: "{e}"' ) try: fc_class = getattr(mod, fc_classname) except AttributeError: raise exceptions.ImproperlyConfigured( f'Module "{fc_module}" does not define a "{fc_classname}" class' ) if not hasattr(fc_class, "signup"): raise exceptions.ImproperlyConfigured( "The custom signup form must offer" " a `def signup(self, request, user)` method", ) return fc_class def prevent_enumeration( request: HttpRequest, email: str | None = None, phone: str | None = None ) -> HttpResponse: login = Login(user=None, email=email, phone=phone, signup=True) return perform_login(context.request, login) def send_unknown_account_mail(request: HttpRequest, email: str) -> None: if not app_settings.EMAIL_UNKNOWN_ACCOUNTS: return None signup_url = get_signup_url(request) context = { "request": request, "signup_url": signup_url, } get_adapter().send_mail("account/email/unknown_account", email, context) def get_signup_url(request: HttpRequest) -> str: url = get_frontend_url(request, "account_signup") if not url: url = build_absolute_uri(request, reverse("account_signup")) return url def complete_signup( request, *, user, email_verification=None, redirect_url=None, signal_kwargs=None, by_passkey=False, ): if signal_kwargs is None: signal_kwargs = {} signals.user_signed_up.send( sender=user.__class__, request=request, user=user, **signal_kwargs ) login = Login( user=user, email_verification=email_verification, redirect_url=redirect_url, signal_kwargs=signal_kwargs, signup=True, ) if by_passkey: login.state["passkey_signup"] = True return perform_login(request, login) ================================================ FILE: allauth/account/internal/stagekit.py ================================================ import time from django.http import HttpResponseRedirect from django.urls import reverse from allauth.account import app_settings from allauth.account.models import Login from allauth.account.stages import LoginStage, LoginStageController LOGIN_SESSION_KEY = "account_login" def get_pending_stage(request) -> LoginStage | None: stage = None if not request.user.is_authenticated: login = unstash_login(request, peek=True) if login: ctrl = LoginStageController(request, login) stage = ctrl.get_pending_stage() return stage def redirect_to_pending_stage(request, stage: LoginStage): if stage.urlname: return HttpResponseRedirect(reverse(stage.urlname)) clear_login(request) return HttpResponseRedirect(reverse("account_login")) def clear_login(request) -> None: request.session.pop(LOGIN_SESSION_KEY, None) def unstash_login(request, peek: bool = False): login = None if peek: data = request.session.get(LOGIN_SESSION_KEY) else: data = request.session.pop(LOGIN_SESSION_KEY, None) if isinstance(data, dict): try: login = Login.deserialize(data) except ValueError: pass else: if time.time() - login.initiated_at > app_settings.LOGIN_TIMEOUT: login = None clear_login(request) else: request._account_login_accessed = True return login def stash_login(request, login) -> None: request.session[LOGIN_SESSION_KEY] = login.serialize() request._account_login_accessed = True ================================================ FILE: allauth/account/internal/userkit.py ================================================ from django.conf import settings from django.contrib.auth import get_user_model from django.contrib.auth.models import AbstractBaseUser from django.core.exceptions import FieldDoesNotExist from django.utils.encoding import force_str from allauth.account import app_settings from allauth.utils import import_callable def user_id_to_str(user) -> str: return user._meta.pk.value_to_string(user) def str_to_user_id(value: str): return get_user_model()._meta.pk.to_python(value) def user_field(user, field, *args, commit=False): """ Gets or sets (optional) user model fields. No-op if fields do not exist. """ if not field: return User = get_user_model() try: field_meta = User._meta.get_field(field) max_length = field_meta.max_length except FieldDoesNotExist: if not hasattr(user, field): return max_length = None if args: # Setter v = args[0] if v: v = v[0:max_length] elif v is None and not field_meta.null: v = "" setattr(user, field, v) if commit: user.save(update_fields=[field]) else: # Getter return getattr(user, field) def did_user_login(user: AbstractBaseUser) -> bool: return user.last_login is not None _user_display_callable = None def default_user_display(user) -> str: ret = "" if app_settings.USER_MODEL_USERNAME_FIELD: ret = getattr(user, app_settings.USER_MODEL_USERNAME_FIELD) return ret or force_str(user) or force_str(user._meta.verbose_name) def user_display(user) -> str: global _user_display_callable if not _user_display_callable: f = getattr(settings, "ACCOUNT_USER_DISPLAY", default_user_display) _user_display_callable = import_callable(f) return _user_display_callable(user) def user_username(user, *args, commit=False): if args and not app_settings.PRESERVE_USERNAME_CASING and args[0]: args = [args[0].lower()] return user_field(user, app_settings.USER_MODEL_USERNAME_FIELD, *args) def user_email(user, *args, commit=False): if args and args[0]: args = [args[0].lower()] ret = user_field(user, app_settings.USER_MODEL_EMAIL_FIELD, *args, commit=commit) if ret: ret = ret.lower() return ret ================================================ FILE: allauth/account/management/__init__.py ================================================ ================================================ FILE: allauth/account/management/commands/__init__.py ================================================ ================================================ FILE: allauth/account/management/commands/account_unsetmultipleprimaryemails.py ================================================ from django.contrib.auth import get_user_model from django.core.management.base import BaseCommand from django.db.models import Count from allauth.account.models import EmailAddress from allauth.account.utils import user_email class Command(BaseCommand): def handle(self, *args, **options): for user in self.get_users_with_multiple_primary_email(): self.unprimary_extra_primary_emails(user) def get_users_with_multiple_primary_email(self): user_pks = [] for email_address_dict in ( EmailAddress.objects.filter(primary=True) .values("user") .annotate(Count("user")) .filter(user__count__gt=1) ): user_pks.append(email_address_dict["user"]) return get_user_model().objects.filter(pk__in=user_pks) def unprimary_extra_primary_emails(self, user): primary_email_addresses = EmailAddress.objects.filter(user=user, primary=True) for primary_email_address in primary_email_addresses: if primary_email_address.email == user_email(user): break else: # Didn't find the main email addresses and break the for loop addresses = ", ".join( [email_address.email for email_address in primary_email_addresses] ) print( f"WARNING: Multiple primary without a user.email match for user pk {user.pk}; " f"(tried: {addresses}, using: {primary_email_address})" ) primary_email_addresses.exclude(pk=primary_email_address.pk).update( primary=False ) ================================================ FILE: allauth/account/managers.py ================================================ from __future__ import annotations from datetime import timedelta from typing import TYPE_CHECKING from django.db import models, transaction from django.db.models import Q from django.http import HttpRequest from django.utils import timezone from . import app_settings if TYPE_CHECKING: from .models import EmailAddress # noqa: F401 class EmailAddressManager(models.Manager["EmailAddress"]): def can_add_email(self, user) -> bool: ret = True if app_settings.CHANGE_EMAIL: # We always allow adding an email in this case, regardless of # `MAX_EMAIL_ADDRESSES`, as adding actually adds a temporary email # that the user wants to change to. return True elif app_settings.MAX_EMAIL_ADDRESSES: count = self.filter(user=user).count() ret = count < app_settings.MAX_EMAIL_ADDRESSES return ret def get_new(self, user): """ Returns the email address the user is in the process of changing to, if any. """ return self.filter(user=user, verified=False).order_by("pk").last() def add_new_email( self, request: HttpRequest, user, email: str, send_verification: bool = True ): """ Adds an email address the user wishes to change to, replacing his current email address once confirmed. """ from allauth.account.internal.flows.email_verification import ( send_verification_email_to_address, ) with transaction.atomic(): instance = self.get_new(user) if instance: instance.remove() email = email.lower() instance = self.create(user=user, email=email) if send_verification: send_verification_email_to_address(request, instance) return instance def add_email(self, request, user, email, confirm=False, signup=False): from allauth.account.internal.flows.email_verification import ( send_verification_email_to_address, ) email = email.lower() email_address, created = self.get_or_create( user=user, email=email, defaults={"email": email} ) if created and confirm: send_verification_email_to_address(request, email_address) return email_address def get_verified(self, user): return self.filter(user=user, verified=True).order_by("-primary", "pk").first() def get_primary(self, user): try: return self.get(user=user, primary=True) except self.model.DoesNotExist: return None def get_primary_email(self, user) -> str | None: from allauth.account.utils import user_email primary = self.get_primary(user) if primary: email = primary.email else: email = user_email(user) return email def get_users_for(self, email): # this is a list rather than a generator because we probably want to # do a len() on it right away return [ address.user for address in self.filter(verified=True, email=email.lower()) ] def fill_cache_for_user(self, user, addresses) -> None: """ In a multi-db setup, inserting records and re-reading them later on may result in not being able to find newly inserted records. Therefore, we maintain a cache for the user so that we can avoid database access when we need to re-read.. """ user._emailaddress_cache = addresses def get_for_user(self, user, email): cache_key = "_emailaddress_cache" addresses = getattr(user, cache_key, None) email = email.lower() if addresses is None: ret = self.get(user=user, email=email.lower()) # To avoid additional lookups when e.g. # EmailAddress.set_as_primary() starts touching self.user ret.user = user return ret else: for address in addresses: if address.email == email: return address raise self.model.DoesNotExist() def is_verified(self, email: str) -> bool: return self.filter(email=email.lower(), verified=True).exists() def lookup(self, emails): return self.filter(email__in=[e.lower() for e in emails]) class EmailConfirmationManager(models.Manager): def all_expired(self): return self.filter(self.expired_q()) def all_valid(self): return self.exclude(self.expired_q()).filter(email_address__verified=False) def expired_q(self): sent_threshold = timezone.now() - timedelta( days=app_settings.EMAIL_CONFIRMATION_EXPIRE_DAYS ) return Q(sent__lt=sent_threshold) def delete_expired_confirmations(self) -> None: self.all_expired().delete() ================================================ FILE: allauth/account/middleware.py ================================================ import os from http import HTTPStatus from types import SimpleNamespace from django.http import HttpResponseRedirect from django.urls import NoReverseMatch, reverse from django.utils.decorators import sync_and_async_middleware from asgiref.sync import iscoroutinefunction, sync_to_async from allauth.account.adapter import get_adapter from allauth.account.internal import flows from allauth.core import context from allauth.core.exceptions import ImmediateHttpResponse, ReauthenticationRequired @sync_and_async_middleware def AccountMiddleware(get_response): if iscoroutinefunction(get_response): async def middleware(request): request.allauth = SimpleNamespace() with context.request_context(request): response = await get_response(request) if _should_redirect_accounts(request, response): response = await _aredirect_accounts(request) return response else: def middleware(request): request.allauth = SimpleNamespace() with context.request_context(request): response = get_response(request) if _should_redirect_accounts(request, response): response = _redirect_accounts(request) return response def process_exception(request, exception): if isinstance(exception, ImmediateHttpResponse): return exception.response elif isinstance(exception, ReauthenticationRequired): redirect_url = reverse("account_login") methods = get_adapter().get_reauthentication_methods(request.user) if methods: redirect_url = methods[0]["url"] return flows.reauthentication.suspend_request(request, redirect_url) middleware.process_exception = process_exception return middleware def _should_redirect_accounts(request, response) -> bool: """ URLs should be hackable. Yet, assuming allauth is included like this... path("accounts/", include("allauth.urls")), ... and a user would attempt to navigate to /accounts/, a 404 would be presented. This code catches that 404, and redirects to either the email management overview or the login page, depending on whether or not the user is authenticated. """ if response.status_code != HTTPStatus.NOT_FOUND: return False try: login_path = reverse("account_login") email_path = reverse("account_email") except NoReverseMatch: # Project might have deviated URLs, let's keep out of the way. return False prefix = os.path.commonprefix([login_path, email_path]) if len(prefix) <= 1 or prefix != request.path: return False # If we have a prefix that is not just '/', and that is what our request is # pointing to, redirect. return True @sync_to_async def _async_get_user(request): return request.user async def _aredirect_accounts(request) -> HttpResponseRedirect: email_path = reverse("account_email") login_path = reverse("account_login") if hasattr(request, "auser"): user = await request.auser() else: # Django <5 user = await _async_get_user(request) return HttpResponseRedirect(email_path if user.is_authenticated else login_path) def _redirect_accounts(request) -> HttpResponseRedirect: email_path = reverse("account_email") login_path = reverse("account_login") user = request.user return HttpResponseRedirect(email_path if user.is_authenticated else login_path) ================================================ FILE: allauth/account/migrations/0001_initial.py ================================================ import django.utils.timezone from django.conf import settings from django.db import migrations, models UNIQUE_EMAIL = getattr(settings, "ACCOUNT_UNIQUE_EMAIL", True) class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name="EmailAddress", fields=[ ( "id", models.AutoField( verbose_name="ID", serialize=False, auto_created=True, primary_key=True, ), ), ( "email", models.EmailField( unique=UNIQUE_EMAIL, max_length=75, verbose_name="email address", ), ), ( "verified", models.BooleanField(default=False, verbose_name="verified"), ), ( "primary", models.BooleanField(default=False, verbose_name="primary"), ), ( "user", models.ForeignKey( verbose_name="user", to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE, ), ), ], options={ "verbose_name": "email address", "verbose_name_plural": "email addresses", }, bases=(models.Model,), ), migrations.CreateModel( name="EmailConfirmation", fields=[ ( "id", models.AutoField( verbose_name="ID", serialize=False, auto_created=True, primary_key=True, ), ), ( "created", models.DateTimeField( default=django.utils.timezone.now, verbose_name="created", ), ), ("sent", models.DateTimeField(null=True, verbose_name="sent")), ( "key", models.CharField(unique=True, max_length=64, verbose_name="key"), ), ( "email_address", models.ForeignKey( verbose_name="email address", to="account.EmailAddress", on_delete=models.CASCADE, ), ), ], options={ "verbose_name": "email confirmation", "verbose_name_plural": "email confirmations", }, bases=(models.Model,), ), ] if not UNIQUE_EMAIL: operations += [ migrations.AlterUniqueTogether( name="emailaddress", unique_together={("user", "email")}, ), ] ================================================ FILE: allauth/account/migrations/0002_email_max_length.py ================================================ from django.conf import settings from django.db import migrations, models UNIQUE_EMAIL = getattr(settings, "ACCOUNT_UNIQUE_EMAIL", True) EMAIL_MAX_LENGTH = getattr(settings, "ACCOUNT_EMAIL_MAX_LENGTH", 254) class Migration(migrations.Migration): dependencies = [ ("account", "0001_initial"), ] operations = [ migrations.AlterField( model_name="emailaddress", name="email", field=models.EmailField( unique=UNIQUE_EMAIL, max_length=EMAIL_MAX_LENGTH, verbose_name="email address", ), ), ] if not UNIQUE_EMAIL: operations += [ migrations.AlterUniqueTogether( name="emailaddress", unique_together={("user", "email")}, ), ] ================================================ FILE: allauth/account/migrations/0003_alter_emailaddress_create_unique_verified_email.py ================================================ # Generated by Django 4.2.2 on 2023-06-14 12:52 from django.conf import settings from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ("account", "0002_email_max_length"), ] operations = ( [ migrations.AlterUniqueTogether( name="emailaddress", unique_together={("user", "email")}, ), migrations.AddConstraint( model_name="emailaddress", constraint=models.UniqueConstraint( condition=models.Q(("verified", True)), fields=["email"], name="unique_verified_email", ), ), ] if getattr(settings, "ACCOUNT_UNIQUE_EMAIL", True) else [] ) ================================================ FILE: allauth/account/migrations/0004_alter_emailaddress_drop_unique_email.py ================================================ from django.conf import settings from django.db import migrations, models EMAIL_MAX_LENGTH = getattr(settings, "ACCOUNT_EMAIL_MAX_LENGTH", 254) class Migration(migrations.Migration): dependencies = [ ("account", "0003_alter_emailaddress_create_unique_verified_email"), ] operations = [ migrations.AlterField( model_name="emailaddress", name="email", field=models.EmailField( max_length=EMAIL_MAX_LENGTH, verbose_name="email address" ), ), ] ================================================ FILE: allauth/account/migrations/0005_emailaddress_idx_upper_email.py ================================================ # Generated by Django 4.2.4 on 2023-08-23 18:17 import django.db.models.functions.text from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("account", "0004_alter_emailaddress_drop_unique_email"), ] operations = [ migrations.AddIndex( model_name="emailaddress", index=models.Index( django.db.models.functions.text.Upper("email"), name="account_emailaddress_upper", ), ), ] ================================================ FILE: allauth/account/migrations/0006_emailaddress_lower.py ================================================ from django.conf import settings from django.db import migrations from django.db.models.functions import Lower from allauth.account import app_settings def forwards(apps, schema_editor): EmailAddress = apps.get_model("account.EmailAddress") User = apps.get_model(settings.AUTH_USER_MODEL) EmailAddress.objects.all().exclude(email=Lower("email")).update( email=Lower("email") ) email_field = app_settings.USER_MODEL_EMAIL_FIELD if email_field: User.objects.all().exclude(**{email_field: Lower(email_field)}).update( **{email_field: Lower(email_field)} ) class Migration(migrations.Migration): dependencies = [ ("account", "0005_emailaddress_idx_upper_email"), ] operations = [migrations.RunPython(forwards, migrations.RunPython.noop)] ================================================ FILE: allauth/account/migrations/0007_emailaddress_idx_email.py ================================================ from django.conf import settings from django.db import migrations, models EMAIL_MAX_LENGTH = getattr(settings, "ACCOUNT_EMAIL_MAX_LENGTH", 254) class Migration(migrations.Migration): dependencies = [ ("account", "0006_emailaddress_lower"), ] operations = [ migrations.RemoveIndex( model_name="emailaddress", name="account_emailaddress_upper", ), migrations.AlterField( model_name="emailaddress", name="email", field=models.EmailField( db_index=True, max_length=EMAIL_MAX_LENGTH, verbose_name="email address" ), ), ] ================================================ FILE: allauth/account/migrations/0008_emailaddress_unique_primary_email_fixup.py ================================================ from django.conf import settings from django.db import migrations from django.db.models import Count def forwards(apps, schema_editor): EmailAddress = apps.get_model("account.EmailAddress") User = apps.get_model(settings.AUTH_USER_MODEL) user_email_field = getattr(settings, "ACCOUNT_USER_MODEL_EMAIL_FIELD", "email") def get_users_with_multiple_primary_email(): user_pks = [] for email_address_dict in ( EmailAddress.objects.filter(primary=True) .values("user") .annotate(Count("user")) .filter(user__count__gt=1) ): user_pks.append(email_address_dict["user"]) return User.objects.filter(pk__in=user_pks) def unset_extra_primary_emails(user): qs = EmailAddress.objects.filter(user=user, primary=True) primary_email_addresses = list(qs) if not primary_email_addresses: return primary_email_address = primary_email_addresses[0] if user_email_field: for address in primary_email_addresses: if address.email.lower() == getattr(user, user_email_field, "").lower(): primary_email_address = address break qs.exclude(pk=primary_email_address.pk).update(primary=False) for user in get_users_with_multiple_primary_email().iterator(2000): unset_extra_primary_emails(user) class Migration(migrations.Migration): dependencies = [ ("account", "0007_emailaddress_idx_email"), ] operations = [ migrations.RunPython(code=forwards, reverse_code=migrations.RunPython.noop) ] ================================================ FILE: allauth/account/migrations/0009_emailaddress_unique_primary_email.py ================================================ # Generated by Django 4.2.11 on 2024-05-09 06:09 from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("account", "0008_emailaddress_unique_primary_email_fixup"), ] operations = [ migrations.AddConstraint( model_name="emailaddress", constraint=models.UniqueConstraint( condition=models.Q(("primary", True)), fields=("user", "primary"), name="unique_primary_email", ), ), ] ================================================ FILE: allauth/account/migrations/__init__.py ================================================ ================================================ FILE: allauth/account/mixins.py ================================================ from django.contrib.auth import REDIRECT_FIELD_NAME from django.core.exceptions import ImproperlyConfigured from django.http import HttpResponsePermanentRedirect, HttpResponseRedirect from django.utils.decorators import method_decorator from django.utils.html import format_html from django.views.decorators.cache import never_cache from allauth.account import app_settings from allauth.account.adapter import get_adapter from allauth.account.internal import flows from allauth.account.internal.decorators import login_not_required from allauth.account.internal.stagekit import ( get_pending_stage, redirect_to_pending_stage, ) from allauth.account.utils import ( get_login_redirect_url, get_next_redirect_url, passthrough_next_redirect_url, ) from allauth.core.exceptions import ImmediateHttpResponse from allauth.utils import get_request_param def _ajax_response(request, response, form=None, data=None): adapter = get_adapter() if adapter.is_ajax(request): if isinstance(response, HttpResponseRedirect) or isinstance( response, HttpResponsePermanentRedirect ): redirect_to = response["Location"] else: redirect_to = None response = adapter.ajax_response( request, response, form=form, data=data, redirect_to=redirect_to ) return response class RedirectAuthenticatedUserMixin: @method_decorator(login_not_required) @method_decorator(never_cache) def dispatch(self, request, *args, **kwargs): if app_settings.AUTHENTICATED_LOGIN_REDIRECTS: if request.user.is_authenticated: redirect_to = self.get_authenticated_redirect_url() response = HttpResponseRedirect(redirect_to) return _ajax_response(request, response) else: stage = get_pending_stage(request) if stage and stage.is_resumable(request): return redirect_to_pending_stage(request, stage) response = super().dispatch(request, *args, **kwargs) return response def get_authenticated_redirect_url(self): redirect_field_name = self.redirect_field_name return get_login_redirect_url( self.request, url=self.get_success_url(), redirect_field_name=redirect_field_name, ) class LogoutFunctionalityMixin: def logout(self): flows.logout.logout(self.request) class AjaxCapableProcessFormViewMixin: def get(self, request, *args, **kwargs): response = super().get(request, *args, **kwargs) form = self.get_form() return _ajax_response( self.request, response, form=form, data=self._get_ajax_data_if() ) def post(self, request, *args, **kwargs): form_class = self.get_form_class() form = self.get_form(form_class) if form.is_valid(): response = self.form_valid(form) else: response = self.form_invalid(form) return _ajax_response( self.request, response, form=form, data=self._get_ajax_data_if() ) def get_form(self, form_class=None): form = getattr(self, "_cached_form", None) if form is None: form = super().get_form(form_class) self._cached_form = form return form def _get_ajax_data_if(self): return ( self.get_ajax_data() if get_adapter(self.request).is_ajax(self.request) else None ) def get_ajax_data(self): return None class CloseableSignupMixin: template_name_signup_closed = ( f"account/signup_closed.{app_settings.TEMPLATE_EXTENSION}" ) def dispatch(self, request, *args, **kwargs): try: if not self.is_open(): return self.closed() except ImmediateHttpResponse as e: return e.response return super().dispatch(request, *args, **kwargs) def is_open(self): return get_adapter(self.request).is_open_for_signup(self.request) def closed(self): response_kwargs = { "request": self.request, "template": self.template_name_signup_closed, } return self.response_class(**response_kwargs) class NextRedirectMixin: redirect_field_name = REDIRECT_FIELD_NAME def get_context_data(self, **kwargs): ret = super().get_context_data(**kwargs) redirect_field_value = get_request_param(self.request, self.redirect_field_name) ret.update( { "redirect_field_name": self.redirect_field_name, "redirect_field_value": redirect_field_value, "redirect_field": ( format_html( '', self.redirect_field_name, redirect_field_value, ) if redirect_field_value else "" ), } ) return ret def get_success_url(self): """ We're in a mixin, so we cannot rely on the fact that our super() has a get_success_url. Also, we want to check for -- in this order: 1) The `?next=/foo` 2) The `get_succes_url()` if available. 3) The `.success_url` if available. 4) A fallback default success URL: `get_default_success_url()`. """ url = self.get_next_url() if url: return url if not url: if hasattr(super(), "get_success_url"): try: url = super().get_success_url() except ImproperlyConfigured: # Django's default get_success_url() checks self.succes_url, # and throws this if that is not set. Yet, in our case, we # want to fallback to the default. pass elif hasattr(self, "success_url"): url = self.success_url if url: url = str(url) # reverse_lazy if not url: url = self.get_default_success_url() return url def get_default_success_url(self): return None def get_next_url(self): return get_next_redirect_url(self.request, self.redirect_field_name) def passthrough_next_url(self, url): return passthrough_next_redirect_url( self.request, url, self.redirect_field_name ) ================================================ FILE: allauth/account/models.py ================================================ from __future__ import annotations import datetime import time from typing import TYPE_CHECKING, Any, TypedDict from django.conf import settings from django.contrib.auth import get_user_model from django.contrib.auth.models import AbstractBaseUser from django.core import signing from django.db import models from django.db.models import Q from django.db.models.constraints import UniqueConstraint from django.utils import timezone from django.utils.translation import gettext_lazy as _ from allauth.account import app_settings from allauth.account.adapter import get_adapter from allauth.account.managers import EmailAddressManager, EmailConfirmationManager if TYPE_CHECKING: from django.http import HttpRequest class EmailAddress(models.Model): user = models.ForeignKey( settings.AUTH_USER_MODEL, verbose_name=_("user"), on_delete=models.CASCADE, ) email = models.EmailField( db_index=True, max_length=app_settings.EMAIL_MAX_LENGTH, verbose_name=_("email address"), ) verified = models.BooleanField(verbose_name=_("verified"), default=False) primary = models.BooleanField(verbose_name=_("primary"), default=False) objects = EmailAddressManager() class Meta: verbose_name = _("email address") verbose_name_plural = _("email addresses") unique_together = [("user", "email")] constraints = [ UniqueConstraint( fields=["user", "primary"], name="unique_primary_email", condition=Q(primary=True), ) ] if app_settings.UNIQUE_EMAIL: constraints.append( UniqueConstraint( fields=["email"], name="unique_verified_email", condition=Q(verified=True), ) ) def __str__(self) -> str: return self.email def clean(self) -> None: super().clean() self.email = self.email.lower() def can_set_verified(self) -> bool: if self.verified: return True conflict = False if app_settings.UNIQUE_EMAIL: conflict = ( EmailAddress.objects.exclude(pk=self.pk) .filter(verified=True, email=self.email) .exists() ) return not conflict def set_verified(self, commit: bool = True) -> bool: if self.verified: return True if self.can_set_verified(): self.verified = True if commit: self.save(update_fields=["verified"]) return self.verified def set_as_primary(self, conditional: bool = False) -> bool: """Marks the email address as primary. In case of `conditional`, it is only marked as primary if there is no other primary email address set. """ from allauth.account.utils import user_email old_primary = EmailAddress.objects.get_primary(self.user) if old_primary: if conditional: return False old_primary.primary = False old_primary.save() self.primary = True self.save() user_email(self.user, self.email, commit=True) return True def send_confirmation( self, request: HttpRequest | None = None, signup: bool = False ) -> EmailConfirmation | EmailConfirmationHMAC: model = get_emailconfirmation_model() confirmation = model.create(self) confirmation.send(request, signup=signup) return confirmation def remove(self) -> None: from allauth.account.utils import user_email self.delete() if user_email(self.user) == self.email: alt = ( EmailAddress.objects.filter(user=self.user) .order_by("-verified") .first() ) alt_email = "" if alt: alt_email = alt.email user_email(self.user, alt_email, commit=True) class EmailConfirmationMixin: def confirm(self, request: HttpRequest) -> EmailAddress | None: from allauth.account.internal.flows.email_verification import ( mark_email_address_as_verified, ) email_address = self.email_address # type: ignore[attr-defined] return mark_email_address_as_verified(request, email_address) def send(self, request: HttpRequest | None = None, signup: bool = False) -> None: get_adapter().send_confirmation_mail(request, self, signup) class EmailConfirmation(EmailConfirmationMixin, models.Model): email_address = models.ForeignKey( EmailAddress, verbose_name=_("email address"), on_delete=models.CASCADE, ) created = models.DateTimeField(verbose_name=_("created"), default=timezone.now) sent = models.DateTimeField(verbose_name=_("sent"), null=True) key = models.CharField(verbose_name=_("key"), max_length=64, unique=True) objects = EmailConfirmationManager() class Meta: verbose_name = _("email confirmation") verbose_name_plural = _("email confirmations") def __str__(self) -> str: return f"confirmation for {self.email_address}" @classmethod def create(cls, email_address: EmailAddress) -> EmailConfirmation: key = get_adapter().generate_emailconfirmation_key(email_address.email) return cls._default_manager.create(email_address=email_address, key=key) @classmethod def from_key(cls, key: str) -> EmailConfirmation | None: qs = EmailConfirmation.objects.all_valid() qs = qs.select_related("email_address__user") emailconfirmation = qs.filter(key=key.lower()).first() return emailconfirmation def key_expired(self) -> bool: assert self.sent is not None # nosec[assert_used] expiration_date = self.sent + datetime.timedelta( days=app_settings.EMAIL_CONFIRMATION_EXPIRE_DAYS ) return expiration_date <= timezone.now() key_expired.boolean = True # type: ignore[attr-defined] def confirm(self, request: HttpRequest) -> EmailAddress | None: if not self.key_expired(): return super().confirm(request) return None def send(self, request: HttpRequest | None = None, signup: bool = False) -> None: super().send(request=request, signup=signup) self.sent = timezone.now() self.save() class EmailConfirmationHMAC(EmailConfirmationMixin): def __init__(self, email_address: EmailAddress) -> None: self.email_address = email_address @classmethod def create(cls, email_address: EmailAddress) -> EmailConfirmationHMAC: return EmailConfirmationHMAC(email_address) @property def key(self) -> str: return signing.dumps(obj=self.email_address.pk, salt=app_settings.SALT) @classmethod def from_key(cls, key: str) -> EmailConfirmationHMAC | None: try: max_age = 60 * 60 * 24 * app_settings.EMAIL_CONFIRMATION_EXPIRE_DAYS pk = signing.loads(key, max_age=max_age, salt=app_settings.SALT) ret = EmailConfirmationHMAC(EmailAddress.objects.get(pk=pk, verified=False)) except ( signing.SignatureExpired, signing.BadSignature, EmailAddress.DoesNotExist, ): ret = None return ret def key_expired(self) -> bool: return False class _SerializedLogin(TypedDict): user_pk: str | None email_verification: app_settings.EmailVerificationMethod signup: bool redirect_url: str | None email: str | None phone: str | None signal_kwargs: dict[str, Any] | None state: dict[str, Any] initiated_at: float class Login: """ Represents a user that is in the process of logging in. Keyword arguments: signup -- Indicates whether or not sending the email is essential (during signup), or if it can be skipped (e.g. in case email verification is optional and we are only logging in). """ # Optional, because we might be prentending logins to prevent user # enumeration. user: AbstractBaseUser | None email_verification: app_settings.EmailVerificationMethod signal_kwargs: dict | None signup: bool email: str | None phone: str | None state: dict initiated_at: float redirect_url: str | None def __init__( self, user: AbstractBaseUser | None = None, email_verification: app_settings.EmailVerificationMethod | None = None, redirect_url: str | None = None, signal_kwargs: dict | None = None, signup: bool = False, email: str | None = None, state: dict | None = None, initiated_at: float | None = None, phone: str | None = None, ) -> None: self.user = user if not email_verification: email_verification = app_settings.EMAIL_VERIFICATION self.email_verification = email_verification self.redirect_url = redirect_url self.signal_kwargs = signal_kwargs self.signup = signup self.email = email self.phone = phone self.state = {} if state is None else state self.initiated_at = initiated_at if initiated_at else time.time() def serialize(self) -> _SerializedLogin: from allauth.account.utils import user_pk_to_url_str # :-( Knowledge of the `socialaccount` is entering the `account` app. signal_kwargs = self.signal_kwargs if signal_kwargs is not None: sociallogin = signal_kwargs.get("sociallogin") if sociallogin is not None: signal_kwargs = signal_kwargs.copy() signal_kwargs["sociallogin"] = sociallogin.serialize() data: _SerializedLogin = { "user_pk": user_pk_to_url_str(self.user) if self.user else None, "email_verification": self.email_verification, "signup": self.signup, "redirect_url": self.redirect_url, "email": self.email, "phone": self.phone, "signal_kwargs": signal_kwargs, "state": self.state, "initiated_at": self.initiated_at, } return data @classmethod def deserialize(cls, data: dict[str, Any]) -> Login: from allauth.account.utils import url_str_to_user_pk user = None user_pk = data["user_pk"] if user_pk is not None: user = ( get_user_model().objects.filter(pk=url_str_to_user_pk(user_pk)).first() ) try: # :-( Knowledge of the `socialaccount` is entering the `account` app. signal_kwargs = data["signal_kwargs"] if signal_kwargs is not None: sociallogin = signal_kwargs.get("sociallogin") if sociallogin is not None: from allauth.socialaccount.models import SocialLogin signal_kwargs = signal_kwargs.copy() signal_kwargs["sociallogin"] = SocialLogin.deserialize(sociallogin) return Login( user=user, email_verification=data["email_verification"], redirect_url=data["redirect_url"], email=data["email"], phone=data["phone"], signup=data["signup"], signal_kwargs=signal_kwargs, state=data["state"], initiated_at=data["initiated_at"], ) except KeyError: raise ValueError() def get_emailconfirmation_model() -> ( type[EmailConfirmation] | type[EmailConfirmationHMAC] ): if app_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED: raise NotImplementedError elif app_settings.EMAIL_CONFIRMATION_HMAC: return EmailConfirmationHMAC else: return EmailConfirmation ================================================ FILE: allauth/account/reauthentication.py ================================================ import warnings from allauth.account.internal.flows.reauthentication import ( did_recently_authenticate, raise_if_reauthentication_required, ) __all__ = [ "raise_if_reauthentication_required", "did_recently_authenticate", ] warnings.warn("allauth.account.reauthentication is deprecated") ================================================ FILE: allauth/account/signals.py ================================================ from django.contrib.auth.signals import user_logged_out # noqa from django.dispatch import Signal # Provides the arguments "request", "user" user_logged_in = Signal() # Typically followed by `user_logged_in` (unless, email verification kicks in) # Provides the arguments "request", "user" user_signed_up = Signal() # Provides the arguments "request", "user" password_set = Signal() # Provides the arguments "request", "user" password_changed = Signal() # Provides the arguments "request", "user" password_reset = Signal() # Provides the arguments "request", "email_address" email_confirmed = Signal() # Provides the arguments "request", "confirmation", "signup" email_confirmation_sent = Signal() # Provides the arguments "request", "user", "from_email_address", # "to_email_address" email_changed = Signal() # Provides the arguments "request", "user", "email_address" email_added = Signal() # Provides the arguments "request", "user", "email_address" email_removed = Signal() # Provides the arguments "request", "user", "method", and additional method dependent kwargs. authentication_step_completed = Signal() # Internal/private signal. _add_email = Signal() ================================================ FILE: allauth/account/stages.py ================================================ import logging from allauth import app_settings as allauth_settings from allauth.account import app_settings from allauth.account.adapter import get_adapter from allauth.account.app_settings import EmailVerificationMethod from allauth.account.internal.constants import LoginStageKey from allauth.account.internal.flows.email_verification import ( send_verification_email_at_login, ) from allauth.account.models import EmailAddress, Login from allauth.core.internal.httpkit import headed_redirect_response from allauth.utils import import_callable logger = logging.getLogger(__name__) class LoginStage: key: str # Set in subclasses urlname: str | None = None login: Login def __init__(self, controller, request, login): if not self.key: raise ValueError() self.controller = controller self.request = request self.login = login self.state = ( self.login.state.setdefault("stages", {}) .setdefault(self.key, {}) .setdefault("data", {}) ) def handle(self): return None, True def exit(self): from allauth.account.internal.flows.login import resume_login self.controller.set_handled(self.key) return resume_login(self.request, self.login) def abort(self): from allauth.account.internal.stagekit import clear_login clear_login(self.request) return headed_redirect_response("account_login") def is_resumable(self, request): return True class LoginStageController: def __init__(self, request, login): self.request = request self.login = login self.state = self.login.state.setdefault("stages", {}) @classmethod def enter(cls, request, stage_key): from allauth.account.internal.stagekit import unstash_login login = unstash_login(request, peek=True) if not login: return None ctrl = LoginStageController(request, login) if ctrl.state.get("current") != stage_key: return None stages = ctrl.get_stages() for stage in stages: if stage.key == stage_key: return stage return None def set_current(self, stage_key): self.state["current"] = stage_key def is_handled(self, stage_key): return self.state.get(stage_key, {}).get("handled", False) def set_handled(self, stage_key): stage_state = self.state.setdefault(stage_key, {}) stage_state["handled"] = True def get_pending_stage(self) -> LoginStage | None: ret = None stages = self.get_stages() for stage in stages: if self.is_handled(stage.key): continue ret = stage break return ret def get_stage(self, key: str) -> LoginStage | None: try: return next(iter(stage for stage in self.get_stages() if stage.key == key)) except StopIteration: return None def get_stages(self) -> list[LoginStage]: stages = [] adapter = get_adapter(self.request) paths = adapter.get_login_stages() for path in paths: cls = import_callable(path) stage = cls(self, self.request, self.login) stages.append(stage) return stages def handle(self): from allauth.account.internal.stagekit import clear_login, stash_login stages = self.get_stages() for stage in stages: if self.is_handled(stage.key): continue self.set_current(stage.key) response, cont = stage.handle() if response: if cont: stash_login(self.request, self.login) else: clear_login(self.request) return response else: if not cont: # So, on our stages is aborting without actually giving # a response. logger.error("Login stage aborted, redirecting to login") return headed_redirect_response("account_login") self.set_handled(stage.key) clear_login(self.request) class EmailVerificationStage(LoginStage): key = LoginStageKey.VERIFY_EMAIL.value urlname = "account_email_verification_sent" def is_resumable(self, request): return app_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED def handle(self): from allauth.account.utils import has_verified_email response, cont = None, True login = self.login email_verification = login.email_verification if email_verification == EmailVerificationMethod.NONE: pass elif email_verification == EmailVerificationMethod.OPTIONAL: # In case of OPTIONAL verification: send on signup. if not has_verified_email(login.user, login.email) and login.signup: send_verification_email_at_login(self.request, login) elif email_verification == EmailVerificationMethod.MANDATORY: if not has_verified_email(login.user, login.email): send_verification_email_at_login(self.request, login) response = get_adapter().respond_email_verification_sent( self.request, login.user ) return response, cont class LoginByCodeStage(LoginStage): key = LoginStageKey.LOGIN_BY_CODE.value urlname = "account_confirm_login_code" def handle(self): from allauth.account.internal.flows import login_by_code did_initiate_process = bool(self.state) login_by_code_required = get_adapter().is_login_by_code_required(self.login) if not did_initiate_process and not login_by_code_required: # Didn't initiate login by code process, but not required, so continue. return None, True elif self._is_trusted(): return None, True elif not did_initiate_process and login_by_code_required: email = self.login.email or EmailAddress.objects.get_primary_email( self.login.user ) phone = None phone_field = app_settings.SIGNUP_FIELDS.get("phone") if not email and phone_field: phone_verified = get_adapter().get_phone(self.login.user) if phone_verified: phone = phone_verified[0] if not email and not phone: # No way of contacting the user.. cannot meet the # requirements. Abort. return headed_redirect_response("account_login"), False login_by_code.LoginCodeVerificationProcess.initiate( request=self.request, user=self.login.user, phone=phone, email=email, stage=self, ) self.state["login_by_code_required"] = True response = headed_redirect_response("account_confirm_login_code") return response, True def _is_trusted(self) -> bool: if ( not app_settings.LOGIN_BY_CODE_TRUST_ENABLED or not allauth_settings.MFA_ENABLED or self.login.user is None ): return False from allauth.mfa.internal.flows import trust return trust.is_trusted_browser(self.request, self.login.user) class PhoneVerificationStage(LoginStage): key = LoginStageKey.VERIFY_PHONE.value urlname = "account_verify_phone" def handle(self): from allauth.account.internal.flows import phone_verification phone_field = app_settings.SIGNUP_FIELDS.get("phone") if not phone_field: return None, True adapter = get_adapter() if self.login.user: phone_verified = adapter.get_phone(self.login.user) if phone_verified is None: return None, (not phone_field["required"]) phone, verified = phone_verified if verified or not app_settings.PHONE_VERIFICATION_ENABLED: return None, True else: phone = self.login.phone if not phone: return None, True phone_verification.PhoneVerificationStageProcess.initiate( stage=self, phone=phone ) response = headed_redirect_response("account_verify_phone") return response, True ================================================ FILE: allauth/account/static/account/js/account.js ================================================ (function () { const allauth = window.allauth = window.allauth || {} function manageEmailForm (o) { const actions = document.getElementsByName('action_remove') if (actions.length) { actions[0].addEventListener('click', function (e) { if (!window.confirm(o.i18n.confirmDelete)) { e.preventDefault() } }) } } allauth.account = { forms: { manageEmailForm } } })() ================================================ FILE: allauth/account/static/account/js/onload.js ================================================ (function () { document.addEventListener('DOMContentLoaded', function () { Array.from(document.querySelectorAll('script[data-allauth-onload]')).forEach(scriptElt => { const funcRef = scriptElt.dataset.allauthOnload if (typeof funcRef === 'string' && funcRef.startsWith('allauth.')) { const funcArg = JSON.parse(scriptElt.textContent) const func = funcRef.split('.').reduce((acc, part) => acc && acc[part], window) func(funcArg) } }) }) })() ================================================ FILE: allauth/account/templatetags/__init__.py ================================================ ================================================ FILE: allauth/account/templatetags/account.py ================================================ from django import template from allauth.account.utils import user_display register = template.Library() @register.simple_tag(name="user_display") def user_display_tag(user): """ Example usage:: {% user_display user %} or if you need to use in a {% blocktrans %}:: {% user_display user as user_display %} {% blocktrans %} {{ user_display }} has sent you a gift. {% endblocktrans %} """ return user_display(user) ================================================ FILE: allauth/account/urls.py ================================================ from django.conf import settings from django.urls import path, re_path from allauth import app_settings as allauth_app_settings from allauth.account import app_settings from . import views urlpatterns = [ path("login/", views.login, name="account_login"), path("logout/", views.logout, name="account_logout"), path("inactive/", views.account_inactive, name="account_inactive"), ] if not allauth_app_settings.SOCIALACCOUNT_ONLY: urlpatterns.extend( [ path("signup/", views.signup, name="account_signup"), path( "reauthenticate/", views.reauthenticate, name="account_reauthenticate" ), # Email path("email/", views.email, name="account_email"), path( "confirm-email/", views.email_verification_sent, name="account_email_verification_sent", ), path( "password/change/", views.password_change, name="account_change_password", ), path("password/set/", views.password_set, name="account_set_password"), # password reset path( "password/reset/", views.password_reset, name="account_reset_password" ), path( "login/code/confirm/", views.confirm_login_code, name="account_confirm_login_code", ), ] ) if not app_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED: urlpatterns.append( re_path( r"^confirm-email/(?P[-:\w]+)/$", views.confirm_email, name="account_confirm_email", ) ) if "phone" in app_settings.SIGNUP_FIELDS: urlpatterns.extend( [ path( "phone/verify/", views.verify_phone, name="account_verify_phone", ), path( "phone/change/", views.change_phone, name="account_change_phone", ), ] ) if app_settings.PASSWORD_RESET_BY_CODE_ENABLED: urlpatterns.extend( [ path( "password/reset/confirm/", views.confirm_password_reset_code, name="account_confirm_password_reset_code", ), path( "password/reset/complete/", views.complete_password_reset, name="account_complete_password_reset", ), path( "password/reset/done/", views.password_reset_from_key_done, name="account_password_reset_completed", ), ] ) else: urlpatterns.extend( [ path( "password/reset/key/done/", views.password_reset_from_key_done, name="account_reset_password_from_key_done", ), re_path( r"^password/reset/key/(?P[0-9A-Za-z]+)-(?P.+)/$", views.password_reset_from_key, name="account_reset_password_from_key", ), path( "password/reset/done/", views.password_reset_done, name="account_reset_password_done", ), ] ) if getattr(settings, "MFA_PASSKEY_SIGNUP_ENABLED", False): urlpatterns.append( path( "signup/passkey/", views.signup_by_passkey, name="account_signup_by_passkey", ) ) if app_settings.LOGIN_BY_CODE_ENABLED: urlpatterns.extend( [ path( "login/code/", views.request_login_code, name="account_request_login_code", ), ] ) ================================================ FILE: allauth/account/utils.py ================================================ import unicodedata from collections import OrderedDict from django.contrib.auth import REDIRECT_FIELD_NAME, get_user_model from django.db import models from django.db.models import Q from django.utils.http import base36_to_int, int_to_base36 from allauth.account import app_settings from allauth.account.adapter import get_adapter from allauth.account.internal import flows from allauth.account.internal.emailkit import valid_email_or_none from allauth.account.internal.userkit import default_user_display # noqa from allauth.account.internal.userkit import user_display # noqa from allauth.account.internal.userkit import user_email # noqa from allauth.account.internal.userkit import user_field # noqa from allauth.account.internal.userkit import user_username # noqa from allauth.account.models import Login from allauth.core.internal import httpkit from allauth.utils import get_request_param def _unicode_ci_compare(s1: str, s2: str) -> bool: """ Perform case-insensitive comparison of two identifiers, using the recommended algorithm from Unicode Technical Report 36, section 2.11.2(B)(2). """ norm_s1 = unicodedata.normalize("NFKC", s1).casefold() norm_s2 = unicodedata.normalize("NFKC", s2).casefold() return norm_s1 == norm_s2 def get_next_redirect_url( request, redirect_field_name=REDIRECT_FIELD_NAME ) -> str | None: """ Returns the next URL to redirect to, if it was explicitly passed via the request. """ redirect_to = get_request_param(request, redirect_field_name) if redirect_to and not get_adapter().is_safe_url(redirect_to): redirect_to = None return redirect_to def get_login_redirect_url( request, url=None, redirect_field_name=REDIRECT_FIELD_NAME, signup=False ) -> str: ret = url if url and callable(url): # In order to be able to pass url getters around that depend # on e.g. the authenticated state. ret = url() if not ret: ret = get_next_redirect_url(request, redirect_field_name=redirect_field_name) if not ret: if signup: ret = get_adapter().get_signup_redirect_url(request) else: ret = get_adapter().get_login_redirect_url(request) return ret def has_verified_email(user, email=None) -> bool: from .models import EmailAddress emailaddress = None if email: ret = False try: emailaddress = EmailAddress.objects.get_for_user(user, email) ret = emailaddress.verified except EmailAddress.DoesNotExist: pass else: ret = EmailAddress.objects.filter(user=user, verified=True).exists() return ret def perform_login( request, user, email_verification=None, redirect_url=None, signal_kwargs=None, signup=False, email=None, ): login = Login( user=user, email_verification=email_verification, redirect_url=redirect_url, signal_kwargs=signal_kwargs, signup=signup, email=email, ) return flows.login.perform_login(request, login) def complete_signup(request, user, email_verification, success_url, signal_kwargs=None): return flows.signup.complete_signup( request, user=user, email_verification=email_verification, redirect_url=success_url, signal_kwargs=signal_kwargs, ) def cleanup_email_addresses(request, addresses): """ Takes a list of EmailAddress instances and cleans it up, making sure only valid ones remain, without multiple primaries etc. Order is important: e.g. if multiple primary email addresses exist, the first one encountered will be kept as primary. """ from .models import EmailAddress adapter = get_adapter() # Let's group by `email` e2a = OrderedDict() # maps email to EmailAddress primary_addresses = [] verified_addresses = [] primary_verified_addresses = [] for address in addresses: # Pick up only valid ones... email = valid_email_or_none(address.email) if not email: continue address.email = email # `valid_email_or_none` lower cases # ... and non-conflicting ones... if ( app_settings.UNIQUE_EMAIL and app_settings.PREVENT_ENUMERATION != "strict" and EmailAddress.objects.lookup([email]) ): # Email address already exists. continue if ( app_settings.UNIQUE_EMAIL and app_settings.PREVENT_ENUMERATION == "strict" and address.verified and EmailAddress.objects.is_verified(email) ): # Email address already exists, and is verified as well. continue a = e2a.get(email) if a: a.primary = a.primary or address.primary a.verified = a.verified or address.verified else: a = address a.verified = a.verified or adapter.is_email_verified(request, a.email) e2a[email] = a if a.primary: primary_addresses.append(a) if a.verified: primary_verified_addresses.append(a) if a.verified: verified_addresses.append(a) # Now that we got things sorted out, let's assign a primary if primary_verified_addresses: primary_address = primary_verified_addresses[0] elif verified_addresses: # Pick any verified as primary primary_address = verified_addresses[0] elif primary_addresses: # Okay, let's pick primary then, even if unverified primary_address = primary_addresses[0] elif e2a: # Pick the first primary_address = list(e2a.values())[0] else: # Empty primary_address = None # There can only be one primary for a in e2a.values(): a.primary = primary_address.email.lower() == a.email.lower() return list(e2a.values()), primary_address def setup_user_email(request, user, addresses): """ Creates proper EmailAddress for the user that was just signed up. Only sets up, doesn't do any other handling such as sending out email confirmation mails etc. """ from .models import EmailAddress assert not EmailAddress.objects.filter(user=user).exists() # nosec priority_addresses = [] # Is there a stashed email? adapter = get_adapter() stashed_email = adapter.unstash_verified_email(request) if stashed_email: priority_addresses.append( EmailAddress( user=user, email=stashed_email.lower(), primary=True, verified=True ) ) email = user_email(user) if email: priority_addresses.append( EmailAddress(user=user, email=email.lower(), primary=True, verified=False) ) addresses, primary = cleanup_email_addresses( request, priority_addresses + addresses ) for a in addresses: a.user = user a.save() EmailAddress.objects.fill_cache_for_user(user, addresses) if primary and (email or "").lower() != primary.email.lower(): user_email(user, primary.email) user.save() return primary def filter_users_by_username(*username): if app_settings.PRESERVE_USERNAME_CASING: qlist = [ Q(**{f"{app_settings.USER_MODEL_USERNAME_FIELD}__iexact": u}) for u in username ] q = qlist[0] for q2 in qlist[1:]: q = q | q2 ret = get_user_model()._default_manager.filter(q) else: ret = get_user_model()._default_manager.filter( **{ f"{app_settings.USER_MODEL_USERNAME_FIELD}__in": [ u.lower() for u in username ] } ) return ret def filter_users_by_email( email: str, is_active: bool | None = None, prefer_verified: bool = False ) -> list: """Return list of users by email address Typically one, at most just a few in length. First we look through EmailAddress table, than customisable User model table. Add results together avoiding SQL joins and deduplicate. `prefer_verified`: When looking up users by email, there can be cases where users with verified email addresses are preferable above users who did not verify their email address. The password reset is such a use case -- if there is a user with a verified email than that user should be returned, not one of the other users. """ from .models import EmailAddress User = get_user_model() email = email.lower() mails = list(EmailAddress.objects.filter(email=email).select_related("user")) is_verified = False if prefer_verified: verified_mails = list(filter(lambda e: e.verified, mails)) if verified_mails: mails = verified_mails is_verified = True users = [] for e in mails: if _unicode_ci_compare(e.email, email): users.append(e.user) if app_settings.USER_MODEL_EMAIL_FIELD and not is_verified: q_dict = {app_settings.USER_MODEL_EMAIL_FIELD: email} user_qs = User.objects.filter(**q_dict) for user in user_qs.iterator(2000): user_email = getattr(user, app_settings.USER_MODEL_EMAIL_FIELD) if _unicode_ci_compare(user_email, email): users.append(user) if is_active is not None: users = [u for u in set(users) if u.is_active == is_active] return list(set(users)) def passthrough_next_redirect_url(request, url: str, redirect_field_name: str) -> str: next_url = get_next_redirect_url(request, redirect_field_name) if next_url: url = httpkit.add_query_params(url, {redirect_field_name: next_url}) return url def user_pk_to_url_str(user) -> str: """ This should return a string. """ User = get_user_model() pk_field_class = type(User._meta.pk) if issubclass(pk_field_class, models.UUIDField): if isinstance(user.pk, str): return user.pk return user.pk.hex elif issubclass(pk_field_class, models.IntegerField): return int_to_base36(int(user.pk)) return str(user.pk) def url_str_to_user_pk(pk_str): User = get_user_model() remote_field = getattr(User._meta.pk, "remote_field", None) if remote_field and getattr(remote_field, "to", None): pk_field = User._meta.pk.remote_field.to._meta.pk else: pk_field = User._meta.pk pk_field_class = type(pk_field) if issubclass(pk_field_class, models.IntegerField): pk = base36_to_int(pk_str) # always call to_python() -- because there are fields like HashidField # that derive from IntegerField. pk = pk_field.to_python(pk) else: pk = pk_field.to_python(pk_str) return pk ================================================ FILE: allauth/account/views.py ================================================ from http import HTTPStatus from django.contrib import messages from django.contrib.auth.decorators import login_required from django.contrib.sites.shortcuts import get_current_site from django.core.exceptions import PermissionDenied from django.core.validators import validate_email from django.forms import Form, ValidationError from django.http import Http404, HttpResponse, HttpResponseBase, HttpResponseRedirect from django.urls import NoReverseMatch, reverse, reverse_lazy from django.utils.decorators import method_decorator from django.utils.functional import cached_property from django.views.decorators.cache import never_cache from django.views.decorators.debug import sensitive_post_parameters from django.views.generic.base import TemplateView from django.views.generic.edit import FormView from allauth import app_settings as allauth_app_settings from allauth.account import app_settings from allauth.account.adapter import get_adapter from allauth.account.forms import ( AddEmailForm, ChangeEmailForm, ChangePasswordForm, ChangePhoneForm, ConfirmEmailVerificationCodeForm, ConfirmLoginCodeForm, ConfirmPasswordResetCodeForm, LoginForm, ReauthenticateForm, RequestLoginCodeForm, ResetPasswordForm, ResetPasswordKeyForm, SetPasswordForm, SignupForm, UserTokenForm, VerifyPhoneForm, ) from allauth.account.internal import flows from allauth.account.internal.decorators import login_not_required, login_stage_required from allauth.account.internal.flows.email_verification import ( send_verification_email_to_address, ) from allauth.account.mixins import ( AjaxCapableProcessFormViewMixin, CloseableSignupMixin, LogoutFunctionalityMixin, NextRedirectMixin, RedirectAuthenticatedUserMixin, _ajax_response, ) from allauth.account.models import ( EmailAddress, EmailConfirmation, get_emailconfirmation_model, ) from allauth.account.stages import ( EmailVerificationStage, LoginByCodeStage, LoginStageController, PhoneVerificationStage, ) from allauth.account.utils import user_display from allauth.core import ratelimit from allauth.core.exceptions import ImmediateHttpResponse from allauth.core.internal.httpkit import redirect from allauth.core.ratelimit import RateLimited from allauth.decorators import rate_limit from allauth.utils import get_form_class INTERNAL_RESET_SESSION_KEY = "_password_reset_key" sensitive_post_parameters_m = method_decorator( sensitive_post_parameters("oldpassword", "password", "password1", "password2") ) class LoginView( NextRedirectMixin, RedirectAuthenticatedUserMixin, AjaxCapableProcessFormViewMixin, FormView, ): form_class = LoginForm template_name = f"account/login.{app_settings.TEMPLATE_EXTENSION}" success_url = None @method_decorator(rate_limit(action="login")) @method_decorator(login_not_required) @sensitive_post_parameters_m @method_decorator(never_cache) def dispatch(self, request, *args, **kwargs) -> HttpResponseBase: if allauth_app_settings.SOCIALACCOUNT_ONLY and request.method != "GET": raise PermissionDenied() return super().dispatch(request, *args, **kwargs) def get_form_kwargs(self) -> dict: kwargs = super().get_form_kwargs() kwargs["request"] = self.request return kwargs def get_form_class(self): return get_form_class(app_settings.FORMS, "login", self.form_class) def form_valid(self, form) -> HttpResponse: redirect_url = self.get_success_url() try: return form.login(self.request, redirect_url=redirect_url) except ImmediateHttpResponse as e: return e.response def get_context_data(self, **kwargs) -> dict: passkey_login_enabled = False if allauth_app_settings.MFA_ENABLED: from allauth.mfa import app_settings as mfa_settings passkey_login_enabled = mfa_settings.PASSKEY_LOGIN_ENABLED ret = super().get_context_data(**kwargs) signup_url = None if not allauth_app_settings.SOCIALACCOUNT_ONLY: try: signup_url = self.passthrough_next_url(reverse("account_signup")) except NoReverseMatch: # There may project specific tweaks other than # SOCIALACCOUNT_ONLY ... pass site = get_current_site(self.request) ret.update( { "signup_url": signup_url, "site": site, "SOCIALACCOUNT_ENABLED": allauth_app_settings.SOCIALACCOUNT_ENABLED, "SOCIALACCOUNT_ONLY": allauth_app_settings.SOCIALACCOUNT_ONLY, "LOGIN_BY_CODE_ENABLED": app_settings.LOGIN_BY_CODE_ENABLED, "PASSKEY_LOGIN_ENABLED": passkey_login_enabled, } ) if app_settings.LOGIN_BY_CODE_ENABLED: request_login_code_url = self.passthrough_next_url( reverse("account_request_login_code") ) ret["request_login_code_url"] = request_login_code_url return ret login = LoginView.as_view() class SignupView( RedirectAuthenticatedUserMixin, CloseableSignupMixin, NextRedirectMixin, AjaxCapableProcessFormViewMixin, FormView, ): template_name = f"account/signup.{app_settings.TEMPLATE_EXTENSION}" form_class = SignupForm @method_decorator(rate_limit(action="signup")) @method_decorator(login_not_required) @sensitive_post_parameters_m @method_decorator(never_cache) def dispatch(self, request, *args, **kwargs) -> HttpResponseBase: return super().dispatch(request, *args, **kwargs) def get_form_class(self): return get_form_class(app_settings.FORMS, "signup", self.form_class) def form_valid(self, form) -> HttpResponse: self.user, resp = form.try_save(self.request) if resp: return resp try: redirect_url = self.get_success_url() return flows.signup.complete_signup( self.request, user=self.user, redirect_url=redirect_url, by_passkey=form.by_passkey, ) except ImmediateHttpResponse as e: return e.response def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) passkey_signup_enabled = False if allauth_app_settings.MFA_ENABLED: from allauth.mfa import app_settings as mfa_settings passkey_signup_enabled = mfa_settings.PASSKEY_SIGNUP_ENABLED form = ret["form"] email = self.request.session.get("account_verified_email") if email: email_keys = ["email"] if "email2" in app_settings.SIGNUP_FIELDS: email_keys.append("email2") for email_key in email_keys: form.fields[email_key].initial = email login_url = self.passthrough_next_url(reverse("account_login")) signup_url = self.passthrough_next_url(reverse("account_signup")) signup_by_passkey_url = None if passkey_signup_enabled: signup_by_passkey_url = self.passthrough_next_url( reverse("account_signup_by_passkey") ) site = get_current_site(self.request) ret.update( { "login_url": login_url, "signup_url": signup_url, "signup_by_passkey_url": signup_by_passkey_url, "site": site, "SOCIALACCOUNT_ENABLED": allauth_app_settings.SOCIALACCOUNT_ENABLED, "SOCIALACCOUNT_ONLY": allauth_app_settings.SOCIALACCOUNT_ONLY, "PASSKEY_SIGNUP_ENABLED": passkey_signup_enabled, } ) return ret def get_initial(self) -> dict: initial = super().get_initial() email = self.request.GET.get("email") if email: try: validate_email(email) except ValidationError: return initial initial["email"] = email if "email2" in app_settings.SIGNUP_FIELDS: initial["email2"] = email return initial signup = SignupView.as_view() class SignupByPasskeyView(SignupView): template_name = f"account/signup_by_passkey.{app_settings.TEMPLATE_EXTENSION}" def get_form_kwargs(self) -> dict: ret = super().get_form_kwargs() ret["by_passkey"] = True return ret signup_by_passkey = SignupByPasskeyView.as_view() @method_decorator(login_not_required, name="dispatch") class ConfirmEmailView(NextRedirectMixin, LogoutFunctionalityMixin, TemplateView): template_name = f"account/email_confirm.{app_settings.TEMPLATE_EXTENSION}" def get(self, *args, **kwargs) -> HttpResponse: try: self.object = self.get_object() self.logout_other_user(self.object) if app_settings.CONFIRM_EMAIL_ON_GET: return self.post(*args, **kwargs) except Http404: self.object = None ctx = self.get_context_data() if not self.object and get_adapter().is_ajax(self.request): resp = HttpResponse() resp.status_code = HTTPStatus.BAD_REQUEST else: resp = self.render_to_response(ctx) return _ajax_response(self.request, resp, data=self.get_ajax_data()) def logout_other_user(self, confirmation) -> None: """ In the event someone clicks on an email confirmation link for one account while logged into another account, logout of the currently logged in account. """ if ( self.request.user.is_authenticated and self.request.user.pk != confirmation.email_address.user_id ): self.logout() def post(self, *args, **kwargs) -> HttpResponse: self.object = verification = self.get_object() email_address, response = flows.email_verification.verify_email_and_resume( self.request, verification ) if response: return response if not email_address: return self.respond(False) self.logout_other_user(self.object) return self.respond(True) def respond(self, success): redirect_url = self.get_redirect_url() if not redirect_url: ctx = self.get_context_data() return self.render_to_response(ctx) return redirect(redirect_url) def get_object(self, queryset=None): key = self.kwargs["key"] model = get_emailconfirmation_model() emailconfirmation = model.from_key(key) if not emailconfirmation: raise Http404() return emailconfirmation def get_queryset(self): qs = EmailConfirmation.objects.all_valid() qs = qs.select_related("email_address__user") return qs def get_ajax_data(self) -> dict: ret: dict = { "can_confirm": bool(self.object), } if self.object: ret["email"] = self.object.email_address.email ret["user"] = {"display": user_display(self.object.email_address.user)} return ret def get_context_data(self, **kwargs) -> dict: ctx = super().get_context_data(**kwargs) site = get_current_site(self.request) ctx.update( { "site": site, "confirmation": self.object, "can_confirm": self.object and self.object.email_address.can_set_verified(), } ) if self.object: ctx["email"] = self.object.email_address.email return ctx def get_redirect_url(self) -> str: url = self.get_next_url() if not url: url = get_adapter(self.request).get_email_verification_redirect_url( self.object.email_address, ) return url confirm_email = ConfirmEmailView.as_view() @method_decorator(login_required, name="dispatch") @method_decorator(rate_limit(action="manage_email"), name="dispatch") class EmailView(AjaxCapableProcessFormViewMixin, FormView): template_name = ( f"account/email_change.{app_settings.TEMPLATE_EXTENSION}" if app_settings.CHANGE_EMAIL else f"account/email.{app_settings.TEMPLATE_EXTENSION}" ) form_class = AddEmailForm success_url = reverse_lazy("account_email") def get_form_class(self): return get_form_class(app_settings.FORMS, "add_email", self.form_class) def dispatch(self, request, *args, **kwargs) -> HttpResponseBase: self._did_send_verification_email = False flows.manage_email.sync_user_email_address(request.user) return super().dispatch(request, *args, **kwargs) def get_form_kwargs(self) -> dict: kwargs = super().get_form_kwargs() kwargs["user"] = self.request.user return kwargs def form_valid(self, form) -> HttpResponse: flows.manage_email.add_email(self.request, form) self._did_send_verification_email = True return super().form_valid(form) def post(self, request, *args, **kwargs) -> HttpResponse: res = None if "action_add" in request.POST: res = super().post(request, *args, **kwargs) elif request.POST.get("email"): if "action_send" in request.POST: res = self._action_send(request) elif "action_remove" in request.POST: res = self._action_remove(request) elif "action_primary" in request.POST: res = self._action_primary(request) res = res or HttpResponseRedirect(self.get_success_url()) # Given that we bypassed AjaxCapableProcessFormViewMixin, # we'll have to call invoke it manually... res = _ajax_response(request, res, data=self._get_ajax_data_if()) else: # No email address selected res = HttpResponseRedirect(self.success_url) res = _ajax_response(request, res, data=self._get_ajax_data_if()) return res def _get_email_address(self, request): email = request.POST["email"] try: validate_email(email) except ValidationError: return None try: return EmailAddress.objects.get_for_user(user=request.user, email=email) except EmailAddress.DoesNotExist: pass def _action_send(self, request, *args, **kwargs): email_address = self._get_email_address(request) did_send_verification_email = False if email_address: did_send_verification_email = send_verification_email_to_address( self.request, email_address ) self._did_send_verification_email = did_send_verification_email if ( app_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED and did_send_verification_email ): return HttpResponseRedirect(reverse("account_email_verification_sent")) def _action_remove(self, request, *args, **kwargs): email_address = self._get_email_address(request) if email_address: if flows.manage_email.delete_email(request, email_address): return HttpResponseRedirect(self.get_success_url()) def _action_primary(self, request, *args, **kwargs): email_address = self._get_email_address(request) if email_address: if flows.manage_email.mark_as_primary(request, email_address): return HttpResponseRedirect(self.get_success_url()) def get_context_data(self, **kwargs): ret = super().get_context_data(**kwargs) emails = list( EmailAddress.objects.filter(user=self.request.user).order_by("email") ) ret.update( { "emailaddresses": emails, "emailaddress_radios": [ { "id": f"email_radio_{i}", "checked": email.primary or len(emails) == 1, "emailaddress": email, } for i, email in enumerate(emails) ], "add_email_form": ret.get("form"), "can_add_email": EmailAddress.objects.can_add_email(self.request.user), } ) if app_settings.CHANGE_EMAIL: ret.update( { "new_emailaddress": EmailAddress.objects.get_new(self.request.user), "current_emailaddress": EmailAddress.objects.get_verified( self.request.user ), } ) return ret def get_ajax_data(self): data = [] for emailaddress in self.request.user.emailaddress_set.all().order_by("pk"): data.append( { "id": emailaddress.pk, "email": emailaddress.email, "verified": emailaddress.verified, "primary": emailaddress.primary, } ) return data def get_success_url(self): if ( self._did_send_verification_email and app_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED ): return reverse("account_email_verification_sent") return self.success_url email = EmailView.as_view() @method_decorator(login_required, name="dispatch") @method_decorator(rate_limit(action="change_password"), name="dispatch") class PasswordChangeView(AjaxCapableProcessFormViewMixin, NextRedirectMixin, FormView): template_name = f"account/password_change.{app_settings.TEMPLATE_EXTENSION}" form_class = ChangePasswordForm def get_form_class(self): return get_form_class(app_settings.FORMS, "change_password", self.form_class) @sensitive_post_parameters_m def dispatch(self, request, *args, **kwargs): if not self.request.user.has_usable_password(): return HttpResponseRedirect(reverse("account_set_password")) return super().dispatch(request, *args, **kwargs) def get_form_kwargs(self) -> dict: kwargs = super().get_form_kwargs() kwargs["user"] = self.request.user return kwargs def get_default_success_url(self): return get_adapter().get_password_change_redirect_url(self.request) def form_valid(self, form) -> HttpResponse: form.save() flows.password_change.finalize_password_change(self.request, form.user) return super().form_valid(form) def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) # NOTE: For backwards compatibility ret["password_change_form"] = ret.get("form") # (end NOTE) return ret password_change = PasswordChangeView.as_view() @method_decorator(login_required, name="dispatch") @method_decorator( # NOTE: 'change_password' (iso 'set_') is intentional, there is no need to # differentiate between set and change. rate_limit(action="change_password"), name="dispatch", ) class PasswordSetView(AjaxCapableProcessFormViewMixin, NextRedirectMixin, FormView): template_name = f"account/password_set.{app_settings.TEMPLATE_EXTENSION}" form_class = SetPasswordForm def get_form_class(self): return get_form_class(app_settings.FORMS, "set_password", self.form_class) @sensitive_post_parameters_m def dispatch(self, request, *args, **kwargs): if self.request.user.has_usable_password(): return HttpResponseRedirect(reverse("account_change_password")) return super().dispatch(request, *args, **kwargs) def get_form_kwargs(self) -> dict: kwargs = super().get_form_kwargs() kwargs["user"] = self.request.user return kwargs def get_default_success_url(self): return get_adapter().get_password_change_redirect_url(self.request) def form_valid(self, form) -> HttpResponse: form.save() flows.password_change.finalize_password_set(self.request, form.user) return super().form_valid(form) def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) # NOTE: For backwards compatibility ret["password_set_form"] = ret.get("form") # (end NOTE) return ret password_set = PasswordSetView.as_view() @method_decorator(login_not_required, name="dispatch") class PasswordResetView(NextRedirectMixin, AjaxCapableProcessFormViewMixin, FormView): template_name = f"account/password_reset.{app_settings.TEMPLATE_EXTENSION}" form_class = ResetPasswordForm success_url = reverse_lazy("account_reset_password_done") def get_success_url(self) -> str: if not app_settings.PASSWORD_RESET_BY_CODE_ENABLED: return super().get_success_url() return self.passthrough_next_url(reverse("account_confirm_password_reset_code")) def get_form_class(self): return get_form_class(app_settings.FORMS, "reset_password", self.form_class) def form_valid(self, form) -> HttpResponse: r429 = ratelimit.consume_or_429( self.request, action="reset_password", key=form.cleaned_data["email"].lower(), ) if r429: return r429 form.save(self.request) return super().form_valid(form) def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) login_url = self.passthrough_next_url(reverse("account_login")) # NOTE: For backwards compatibility ret["password_reset_form"] = ret.get("form") # (end NOTE) ret.update({"login_url": login_url}) return ret password_reset = PasswordResetView.as_view() @method_decorator(login_not_required, name="dispatch") class PasswordResetDoneView(TemplateView): template_name = f"account/password_reset_done.{app_settings.TEMPLATE_EXTENSION}" password_reset_done = PasswordResetDoneView.as_view() @method_decorator(rate_limit(action="reset_password_from_key"), name="dispatch") @method_decorator(login_not_required, name="dispatch") class PasswordResetFromKeyView( AjaxCapableProcessFormViewMixin, NextRedirectMixin, LogoutFunctionalityMixin, FormView, ): template_name = f"account/password_reset_from_key.{app_settings.TEMPLATE_EXTENSION}" form_class = ResetPasswordKeyForm success_url = reverse_lazy("account_reset_password_from_key_done") reset_url_key = "set-password" def get_form_class(self): return get_form_class( app_settings.FORMS, "reset_password_from_key", self.form_class ) def dispatch(self, request, uidb36, key, **kwargs) -> HttpResponseBase: self.request = request self.key = key user_token_form_class = get_form_class( app_settings.FORMS, "user_token", UserTokenForm ) is_ajax = get_adapter().is_ajax(request) if self.key == self.reset_url_key or is_ajax: if not is_ajax: self.key = self.request.session.get(INTERNAL_RESET_SESSION_KEY, "") # (Ab)using forms here to be able to handle errors in XHR #890 token_form = user_token_form_class(data={"uidb36": uidb36, "key": self.key}) if token_form.is_valid(): self.reset_user = token_form.reset_user # In the event someone clicks on a password reset link # for one account while logged into another account, # logout of the currently logged in account. if ( self.request.user.is_authenticated and self.request.user.pk != self.reset_user.pk ): self.logout() self.request.session[INTERNAL_RESET_SESSION_KEY] = self.key return super().dispatch(request, uidb36, self.key, **kwargs) else: token_form = user_token_form_class(data={"uidb36": uidb36, "key": self.key}) if token_form.is_valid(): # Store the key in the session and redirect to the # password reset form at a URL without the key. That # avoids the possibility of leaking the key in the # HTTP Referer header. self.request.session[INTERNAL_RESET_SESSION_KEY] = self.key redirect_url = self.passthrough_next_url( self.request.path.replace(self.key, self.reset_url_key) ) return redirect(redirect_url) self.reset_user = None response = self.render_to_response(self.get_context_data(token_fail=True)) return _ajax_response(self.request, response, form=token_form) def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) ret["action_url"] = reverse( "account_reset_password_from_key", kwargs={ "uidb36": self.kwargs["uidb36"], "key": self.kwargs["key"], }, ) return ret def get_form_kwargs(self) -> dict: kwargs = super().get_form_kwargs() kwargs["user"] = self.reset_user kwargs["temp_key"] = self.key return kwargs def form_valid(self, form) -> HttpResponse: form.save() resp = flows.password_reset.finalize_password_reset( self.request, self.reset_user ) if resp: return resp return super().form_valid(form) password_reset_from_key = PasswordResetFromKeyView.as_view() @method_decorator(login_not_required, name="dispatch") class PasswordResetFromKeyDoneView(TemplateView): template_name = ( f"account/password_reset_from_key_done.{app_settings.TEMPLATE_EXTENSION}" ) password_reset_from_key_done = PasswordResetFromKeyDoneView.as_view() @method_decorator(rate_limit(action="reset_password_from_key"), name="dispatch") @method_decorator(login_not_required, name="dispatch") class CompletePasswordResetView( NextRedirectMixin, FormView, ): template_name = f"account/password_reset_from_key.{app_settings.TEMPLATE_EXTENSION}" form_class = ResetPasswordKeyForm success_url = reverse_lazy("account_password_reset_completed") def dispatch(self, request, **kwargs) -> HttpResponseBase: self._process = ( flows.password_reset_by_code.PasswordResetVerificationProcess.resume( request ) ) if not self._process: return HttpResponseRedirect( self.passthrough_next_url(reverse("account_reset_password")) ) if not self._process.state.get("code_confirmed"): return HttpResponseRedirect(reverse("account_confirm_password_reset_code")) return super().dispatch(request, **kwargs) def get_form_class(self): return get_form_class( app_settings.FORMS, "reset_password_from_key", self.form_class ) def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) ret["action_url"] = reverse("account_complete_password_reset") return ret def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs["user"] = self._process.user return kwargs def form_valid(self, form): form.save() resp = self._process.finish() if resp: return resp return super().form_valid(form) complete_password_reset = CompletePasswordResetView.as_view() class ConfirmPasswordResetCodeView(NextRedirectMixin, FormView): template_name = ( f"account/confirm_password_reset_code.{app_settings.TEMPLATE_EXTENSION}" ) form_class = ConfirmPasswordResetCodeForm @method_decorator(login_not_required) def dispatch(self, request, *args, **kwargs) -> HttpResponseBase: self._process = ( flows.password_reset_by_code.PasswordResetVerificationProcess.resume( request ) ) if not self._process: return HttpResponseRedirect(reverse("account_login")) if self._process.state.get("code_confirmed"): return HttpResponseRedirect(reverse("account_complete_password_reset")) return super().dispatch(request, *args, **kwargs) def get_form_class(self): return get_form_class( app_settings.FORMS, "confirm_password_reset_code", self.form_class ) def get_form_kwargs(self): ret = super().get_form_kwargs() ret["code"] = self._process.code return ret def get_context_data(self, **kwargs): ret = super().get_context_data(**kwargs) ret["email"] = self._process.state["email"] ret["verify_form"] = ret["form"] return ret def form_valid(self, form): self._process.confirm_code() return HttpResponseRedirect( self.passthrough_next_url(reverse("account_complete_password_reset")) ) def form_invalid(self, form): attempts_left = self._process.record_invalid_attempt() if attempts_left: return super().form_invalid(form) adapter = get_adapter(self.request) adapter.add_message( self.request, messages.ERROR, message=adapter.error_messages["too_many_login_attempts"], ) return HttpResponseRedirect(self.passthrough_next_url(reverse("account_login"))) confirm_password_reset_code = ConfirmPasswordResetCodeView.as_view() class LogoutView(NextRedirectMixin, LogoutFunctionalityMixin, TemplateView): template_name = f"account/logout.{app_settings.TEMPLATE_EXTENSION}" def get(self, *args, **kwargs) -> HttpResponse: if app_settings.LOGOUT_ON_GET: return self.post(*args, **kwargs) if not self.request.user.is_authenticated: response = redirect(self.get_redirect_url()) return _ajax_response(self.request, response) ctx = self.get_context_data() response = self.render_to_response(ctx) return _ajax_response(self.request, response) def post(self, *args, **kwargs) -> HttpResponse: url = self.get_redirect_url() self.logout() response = redirect(url) return _ajax_response(self.request, response) def get_redirect_url(self) -> str: return self.get_next_url() or get_adapter(self.request).get_logout_redirect_url( self.request ) logout = LogoutView.as_view() @method_decorator(login_not_required, name="dispatch") class AccountInactiveView(TemplateView): template_name = f"account/account_inactive.{app_settings.TEMPLATE_EXTENSION}" account_inactive = AccountInactiveView.as_view() @method_decorator(login_not_required, name="dispatch") class EmailVerificationSentView(TemplateView): template_name = f"account/verification_sent.{app_settings.TEMPLATE_EXTENSION}" class ConfirmEmailVerificationCodeView(NextRedirectMixin, FormView): template_name = ( f"account/confirm_email_verification_code.{app_settings.TEMPLATE_EXTENSION}" ) form_class = ConfirmEmailVerificationCodeForm def dispatch(self, request, *args, **kwargs) -> HttpResponseBase: self.stage = LoginStageController.enter(request, EmailVerificationStage.key) self._process = ( flows.email_verification_by_code.EmailVerificationProcess.resume(request) ) # preventing enumeration? verification_is_fake = self._process and "user_id" not in self._process.state # Can we at all continue? if ( # No verification pending? (not self._process) # Anonymous, yet no stage (or fake verifcation)? or ( request.user.is_anonymous and not self.stage and not verification_is_fake ) ): return HttpResponseRedirect( reverse( "account_login" if request.user.is_anonymous else "account_email" ) ) return super().dispatch(request, *args, **kwargs) @cached_property def _action(self): action = self.request.POST.get("action") valid_actions = ["verify"] if self._process.can_change: valid_actions.append("change") if self._process.can_resend: valid_actions.append("resend") if action not in valid_actions: action = "verify" return action def get_form_class(self): if self._action == "change": return self._get_change_form_class() elif self._action == "resend": return Form return self._get_verify_form_class() def _get_change_form_class(self): return get_form_class(app_settings.FORMS, "change_email", ChangeEmailForm) def _get_verify_form_class(self): return get_form_class( app_settings.FORMS, "confirm_email_verification_code", self.form_class ) def get_form_kwargs(self): ret = super().get_form_kwargs() if self._action == "change": pass elif self._action == "verify": ret["code"] = self._process.code if self._process else "" ret["user"] = self._process.user ret["email"] = self._process.email return ret def get_context_data(self, **kwargs): ret = super().get_context_data(**kwargs) ret["can_change"] = self._process.can_change ret["can_resend"] = self._process.can_resend ret["email"] = self._process.state["email"] ret["cancel_url"] = None if self.stage else reverse("account_email") if self._action == "change": ret["change_form"] = ret["form"] ret["verify_form"] = self._get_verify_form_class()() else: ret["change_form"] = self._get_change_form_class()() ret["verify_form"] = ret["form"] return ret def form_valid(self, form) -> HttpResponse: if self._action == "change": return self._change_form_valid(form) elif self._action == "resend": return self._resend_form_valid(form) return self._verify_form_valid(form) def _resend_form_valid(self, form): adapter = get_adapter() try: self._process.resend() except RateLimited: adapter.add_message( self.request, messages.ERROR, message=adapter.error_messages["rate_limited"], ) return HttpResponseRedirect( self.passthrough_next_url(reverse("account_email_verification_sent")) ) def _change_form_valid(self, form): self._process.change_to(form.cleaned_data["email"], form.account_already_exists) return HttpResponseRedirect( self.passthrough_next_url(reverse("account_email_verification_sent")) ) def _verify_form_valid(self, form): email_address = self._process.finish() if self.stage: if not email_address: return self.stage.abort() return self.stage.exit() url = self.get_next_url() if url: pass elif not email_address: url = reverse("account_email") else: url = get_adapter(self.request).get_email_verification_redirect_url( email_address ) return HttpResponseRedirect(url) def form_invalid(self, form): if self._action == "change": return self._change_form_invalid(form) return self._verify_form_invalid(form) def _verify_form_invalid(self, form): attempts_left = self._process.record_invalid_attempt() if attempts_left: return super().form_invalid(form) adapter = get_adapter(self.request) adapter.add_message( self.request, messages.ERROR, message=adapter.error_messages["too_many_login_attempts"], ) return HttpResponseRedirect(reverse("account_login")) @method_decorator(login_not_required, name="dispatch") def email_verification_sent(request) -> HttpResponseBase: if app_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED: return ConfirmEmailVerificationCodeView.as_view()(request) else: return EmailVerificationSentView.as_view()(request) class BaseReauthenticateView(NextRedirectMixin, FormView): def dispatch(self, request, *args, **kwargs) -> HttpResponseBase: resp = self._check_reauthentication_method_available(request) if resp: return resp resp = self._check_ratelimit(request) if resp: return resp return super().dispatch(request, *args, **kwargs) def _check_ratelimit(self, request): return ratelimit.consume_or_429( self.request, action="reauthenticate", user=self.request.user, ) def _check_reauthentication_method_available(self, request): methods = get_adapter().get_reauthentication_methods(self.request.user) if any([m["url"] == request.path for m in methods]): # Method is available return None if not methods: # Reauthentication not available raise PermissionDenied("Reauthentication not available") url = self.passthrough_next_url(methods[0]["url"]) return HttpResponseRedirect(url) def get_default_success_url(self): url = get_adapter(self.request).get_login_redirect_url(self.request) return url def form_valid(self, form) -> HttpResponse: response = flows.reauthentication.resume_request(self.request) if response: return response return super().form_valid(form) def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) ret.update( { "reauthentication_alternatives": self.get_reauthentication_alternatives(), } ) return ret def get_reauthentication_alternatives(self): methods = get_adapter().get_reauthentication_methods(self.request.user) alts = [] for method in methods: alt = dict(method) if self.request.path == alt["url"]: continue alt["url"] = self.passthrough_next_url(alt["url"]) alts.append(alt) alts = sorted(alts, key=lambda alt: alt["description"]) return alts @method_decorator(login_required, name="dispatch") class ReauthenticateView(BaseReauthenticateView): form_class = ReauthenticateForm template_name = f"account/reauthenticate.{app_settings.TEMPLATE_EXTENSION}" def get_form_class(self): return get_form_class(app_settings.FORMS, "reauthenticate", self.form_class) def get_form_kwargs(self) -> dict: ret = super().get_form_kwargs() ret["user"] = self.request.user return ret def form_valid(self, form) -> HttpResponse: flows.reauthentication.reauthenticate_by_password(self.request) return super().form_valid(form) reauthenticate = ReauthenticateView.as_view() class RequestLoginCodeView(RedirectAuthenticatedUserMixin, NextRedirectMixin, FormView): form_class = RequestLoginCodeForm template_name = f"account/request_login_code.{app_settings.TEMPLATE_EXTENSION}" def get_form_class(self): return get_form_class(app_settings.FORMS, "request_login_code", self.form_class) def form_valid(self, form) -> HttpResponse: flows.login_by_code.LoginCodeVerificationProcess.initiate( request=self.request, user=form._user, email=form.cleaned_data.get("email"), phone=form.cleaned_data.get("phone"), ) return super().form_valid(form) def get_success_url(self): if self.request.user.is_authenticated: return None url = reverse_lazy("account_confirm_login_code") url = self.passthrough_next_url(reverse("account_confirm_login_code")) return url def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) site = get_current_site(self.request) ret.update({"site": site}) return ret request_login_code = RequestLoginCodeView.as_view() def _login_by_code_urlname() -> str: # NOTE: Having this as a method instead of a constant allows changing # settings in test cases... return ( "account_request_login_code" if app_settings.LOGIN_BY_CODE_ENABLED else "account_login" ) @method_decorator( login_stage_required( stage=LoginByCodeStage.key, redirect_urlname=(_login_by_code_urlname()) ), name="dispatch", ) class ConfirmLoginCodeView(NextRedirectMixin, FormView): form_class = ConfirmLoginCodeForm template_name = f"account/confirm_login_code.{app_settings.TEMPLATE_EXTENSION}" @method_decorator(never_cache) def dispatch(self, request, *args, **kwargs) -> HttpResponseBase: self.stage = request._login_stage self._process = flows.login_by_code.LoginCodeVerificationProcess.resume( self.stage ) if not self._process: return HttpResponseRedirect(reverse(_login_by_code_urlname())) return super().dispatch(request, *args, **kwargs) @cached_property def _action(self) -> str: action = self.request.POST.get("action") valid_actions = ["verify"] if self._process.can_resend: valid_actions.append("resend") if action not in valid_actions: action = "verify" return action def get_form_class(self): if self._action == "resend": return Form return get_form_class(app_settings.FORMS, "confirm_login_code", self.form_class) def get_form_kwargs(self) -> dict: kwargs = super().get_form_kwargs() if self._action == "resend": pass else: kwargs["code"] = self._process.code return kwargs def form_valid(self, form) -> HttpResponse: if self._action == "resend": return self._resend_form_valid(form) return self._verify_form_valid(form) def _resend_form_valid(self, form) -> HttpResponse: try: self._process.resend() except RateLimited: adapter = get_adapter() adapter.add_message( self.request, messages.ERROR, message=adapter.error_messages["rate_limited"], ) return HttpResponseRedirect( self.passthrough_next_url(reverse("account_confirm_login_code")) ) def _verify_form_valid(self, form) -> HttpResponse: redirect_url = self.get_next_url() return self._process.finish(redirect_url) def form_invalid(self, form) -> HttpResponse: attempts_left = self._process.record_invalid_attempt() if attempts_left: return super().form_invalid(form) adapter = get_adapter(self.request) adapter.add_message( self.request, messages.ERROR, message=adapter.error_messages["too_many_login_attempts"], ) return HttpResponseRedirect( reverse( _login_by_code_urlname() if self._process.state["initiated_by_user"] else "account_login" ) ) def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) site = get_current_site(self.request) email = self._process.state.get("email") phone = self._process.state.get("phone") ret.update( { "can_resend": self._process.can_resend, "site": site, "email": email, "phone": phone, "verify_form": ret["form"], } ) return ret confirm_login_code = ConfirmLoginCodeView.as_view() class _BaseVerifyPhoneView(NextRedirectMixin, FormView): form_class = VerifyPhoneForm template_name = ( f"account/confirm_phone_verification_code.{app_settings.TEMPLATE_EXTENSION}" ) @cached_property def _action(self): action = self.request.POST.get("action") valid_actions = ["verify"] if self.process.can_change: valid_actions.append("change") if self.process.can_resend: valid_actions.append("resend") if action not in valid_actions: action = "verify" return action def get_form_class(self): if self._action == "change": return self._get_change_form_class() elif self._action == "resend": return Form return self._get_verify_form_class() def _get_change_form_class(self): return get_form_class(app_settings.FORMS, "change_phone", ChangePhoneForm) def _get_verify_form_class(self): return get_form_class(app_settings.FORMS, "verify_phone", self.form_class) def get_form_kwargs(self): kwargs = super().get_form_kwargs() if self._action == "change": kwargs["phone"] = self.process.phone kwargs["user"] = self.process.user elif self._action == "resend": pass else: kwargs["code"] = self.process.code kwargs["phone"] = self.process.phone kwargs["user"] = self.process.user return kwargs def form_valid(self, form) -> HttpResponse: if self._action == "change": return self._change_form_valid(form) elif self._action == "resend": return self._resend_form_valid(form) return self._verify_form_valid(form) def _resend_form_valid(self, form): try: self.process.resend() except RateLimited: adapter = get_adapter() adapter.add_message( self.request, messages.ERROR, message=adapter.error_messages["rate_limited"], ) return HttpResponseRedirect( self.passthrough_next_url(reverse("account_verify_phone")) ) def _change_form_valid(self, form): self.process.change_to(form.cleaned_data["phone"], form.account_already_exists) return HttpResponseRedirect( self.passthrough_next_url(reverse("account_verify_phone")) ) def _verify_form_valid(self, form): self.process.finish() return self.respond_process_succeeded(form) def form_invalid(self, form) -> HttpResponse: if self._action == "change": return self._change_form_invalid(form) return self._verify_form_invalid(form) def _change_form_invalid(self, form): return super().form_invalid(form) def _verify_form_invalid(self, form): attempts_left = self.process.record_invalid_attempt() if attempts_left: return super().form_invalid(form) self.process.abort() return self.respond_process_failed(form) def get_context_data(self, **kwargs): ret = super().get_context_data(**kwargs) ret["can_change"] = self.process.can_change ret["can_resend"] = self.process.can_resend site = get_current_site(self.request) if self._action == "change": ret["change_form"] = ret["form"] ret["verify_form"] = self._get_verify_form_class()() else: ret["change_form"] = self._get_change_form_class()() ret["verify_form"] = ret["form"] ret.update( { "site": site, "phone": self.process.phone, "action": self._action, } ) return ret @method_decorator( login_stage_required( stage=PhoneVerificationStage.key, redirect_urlname="account_login" ), name="dispatch", ) class _VerifyPhoneSignupView(_BaseVerifyPhoneView): @method_decorator(never_cache) def dispatch(self, request, *args, **kwargs) -> HttpResponseBase: self.stage = request._login_stage self.process = flows.phone_verification.PhoneVerificationStageProcess.resume( self.stage ) if not self.process: return self.stage.abort() return super().dispatch(request, *args, **kwargs) def respond_process_succeeded(self, form): return self.stage.exit() def respond_process_failed(self, form): adapter = get_adapter(self.request) adapter.add_message( self.request, messages.ERROR, message=adapter.error_messages["too_many_login_attempts"], ) return self.stage.abort() class _VerifyPhoneChangeView(_BaseVerifyPhoneView): @method_decorator(never_cache) def dispatch(self, request, *args, **kwargs) -> HttpResponseBase: self.process = flows.phone_verification.ChangePhoneVerificationProcess.resume( request ) if not self.process: return HttpResponseRedirect(reverse("account_change_phone")) return super().dispatch(request, *args, **kwargs) def respond_process_succeeded(self, form): return HttpResponseRedirect(reverse("account_change_phone")) def respond_process_failed(self, form): return HttpResponseRedirect(reverse("account_change_phone")) def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) ret.update({"cancel_url": reverse("account_change_phone")}) return ret @method_decorator(login_not_required, name="dispatch") def verify_phone(request): if request.user.is_authenticated: return _VerifyPhoneChangeView.as_view()(request) return _VerifyPhoneSignupView.as_view()(request) @method_decorator(login_required, name="dispatch") @method_decorator(rate_limit(action="change_phone"), name="dispatch") class ChangePhoneView(FormView): template_name = f"account/phone_change.{app_settings.TEMPLATE_EXTENSION}" form_class = ChangePhoneForm success_url = reverse_lazy("account_verify_phone") def get_form_class(self): return get_form_class(app_settings.FORMS, "change_phone", self.form_class) def get_form_kwargs(self) -> dict: ret = super().get_form_kwargs() self._phone_verified = get_adapter().get_phone(self.request.user) if ( self.request.POST.get("action") == "verify" and self._phone_verified and not self._phone_verified[1] ): # We're (re-)sending the verificaton code, so just feed the existing # phone to the form... ret["data"] = {"phone": self._phone_verified[0]} ret["phone"] = None else: ret["phone"] = self._phone_verified[0] if self._phone_verified else None ret["user"] = self.request.user return ret def form_valid(self, form) -> HttpResponse: flows.phone_verification.ChangePhoneVerificationProcess.initiate( self.request, form.cleaned_data["phone"] ) return super().form_valid(form) def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) phone = None phone_verified = False if self._phone_verified: phone, phone_verified = self._phone_verified ret.update( { "phone": phone, "phone_verified": phone_verified, } ) return ret change_phone = ChangePhoneView.as_view() ================================================ FILE: allauth/app_settings.py ================================================ from django.apps import apps from allauth.core.internal.cryptokit import UserCodeFormat class AppSettings: def __init__(self, prefix: str) -> None: self.prefix = prefix def _setting(self, name: str, dflt): from allauth.utils import get_setting return get_setting(self.prefix + name, dflt) @property def SITES_ENABLED(self) -> bool: return apps.is_installed("django.contrib.sites") @property def SOCIALACCOUNT_ENABLED(self) -> bool: return apps.is_installed("allauth.socialaccount") @property def SOCIALACCOUNT_ONLY(self) -> bool: from allauth.utils import get_setting return get_setting("SOCIALACCOUNT_ONLY", False) @property def MFA_ENABLED(self) -> bool: return apps.is_installed("allauth.mfa") @property def USERSESSIONS_ENABLED(self) -> bool: return apps.is_installed("allauth.usersessions") @property def HEADLESS_ENABLED(self) -> bool: return apps.is_installed("allauth.headless") @property def HEADLESS_ONLY(self) -> bool: from allauth.utils import get_setting return get_setting("HEADLESS_ONLY", False) @property def DEFAULT_AUTO_FIELD(self): return self._setting("DEFAULT_AUTO_FIELD", None) @property def TRUSTED_PROXY_COUNT(self) -> int: """ As the ``X-Forwarded-For`` header can be spoofed, you need to configure the number of proxies that are under your control and hence, can be trusted. The default is 0, meaning, no proxies are trusted. As a result, the ``X-Forwarded-For`` header will be disregarded by default. """ return self._setting("TRUSTED_PROXY_COUNT", 0) @property def TRUSTED_CLIENT_IP_HEADER(self) -> str | None: """ If your service is running behind a trusted proxy that sets a custom header containing the client IP address, specify that header name here. The client IP will be extracted from this header instead of ``X-Forwarded-For``. Examples: ``"CF-Connecting-IP"`` (Cloudflare), ``"X-Real-IP"`` (nginx). """ return self._setting("TRUSTED_CLIENT_IP_HEADER", None) @property def USER_CODE_FORMAT(self) -> UserCodeFormat: """ Controls the format of user-facing verification codes (e.g. email verification, phone verification, login codes). """ return self._setting("USER_CODE_FORMAT", {}) _app_settings = AppSettings("ALLAUTH_") def __getattr__(name): # See https://peps.python.org/pep-0562/ return getattr(_app_settings, name) ================================================ FILE: allauth/core/__init__.py ================================================ ================================================ FILE: allauth/core/context.py ================================================ from contextlib import contextmanager from contextvars import ContextVar _request_var = ContextVar("request", default=None) def __getattr__(name): if name == "request": return _request_var.get() raise AttributeError(name) @contextmanager def request_context(request): token = _request_var.set(request) try: yield finally: _request_var.reset(token) ================================================ FILE: allauth/core/exceptions.py ================================================ class ImmediateHttpResponse(Exception): """ This exception is used to interrupt the flow of processing to immediately return a custom HttpResponse. """ def __init__(self, response): self.response = response class ReauthenticationRequired(Exception): """ The action could not be performed because the user needs to reauthenticate. """ pass class SignupClosedException(Exception): """ Throws when attemtping to signup while signup is closed. """ pass class RateLimited(Exception): pass ================================================ FILE: allauth/core/internal/__init__.py ================================================ ================================================ FILE: allauth/core/internal/adapter.py ================================================ from django.core.exceptions import ValidationError from allauth.core import context class BaseAdapter: def __init__(self, request=None): # Explicitly passing `request` is deprecated, just use: # `allauth.core.context.request`. self.request = context.request def validation_error(self, code, *args): message = self.error_messages[code] if args: message = message % args exc = ValidationError(message, code=code) return exc ================================================ FILE: allauth/core/internal/cryptokit.py ================================================ import math import string from typing import TypedDict from django.utils.crypto import get_random_string class UserCodeFormat(TypedDict, total=False): dashed: bool length: int numeric: bool # https://datatracker.ietf.org/doc/html/rfc8628#section-6.1 _ALPHA_ALLOWED_CHARS = "BCDFGHJKLMNPQRSTVWXZ" def generate_user_code( *, length: int | None = None, numeric: bool = False, allowed_chars: str | None = None, dashed: bool = True, ) -> str: """ """ if not allowed_chars: if numeric: allowed_chars = string.digits else: allowed_chars = _ALPHA_ALLOWED_CHARS if length is None: length = 9 if numeric else 8 code = get_random_string(length=length, allowed_chars=allowed_chars) if dashed: parts = 2 if length <= 8 else 3 chunk = math.ceil(length / parts) code = "-".join(code[i : i + chunk] for i in range(0, length, chunk)) return code def _strip_punctuation(code: str) -> str: """https://datatracker.ietf.org/doc/html/rfc8628#section-6.1 When processing the inputted user code, the server should strip dashes and other punctuation that it added for readability (making the inclusion of such punctuation by the user optional). """ return code.translate(str.maketrans("", "", string.punctuation + string.whitespace)) def compare_user_code(*, actual: str, expected: str) -> bool: actual = _strip_punctuation(actual).lower() expected = _strip_punctuation(expected).lower() return bool(expected) and actual == expected ================================================ FILE: allauth/core/internal/httpkit.py ================================================ import ipaddress import json from urllib.parse import parse_qs, quote, urlencode, urlparse, urlunparse from django import shortcuts from django.core.exceptions import ImproperlyConfigured from django.http import ( HttpRequest, HttpResponseRedirect, HttpResponseServerError, QueryDict, ) from django.http.request import split_domain_port from django.urls import NoReverseMatch, reverse from allauth import app_settings as allauth_settings HTTP_USER_AGENT_MAX_LENGTH = 200 def serialize_request(request): return json.dumps( { "path": request.path, "path_info": request.path_info, "META": {k: v for k, v in request.META.items() if isinstance(v, str)}, "GET": request.GET.urlencode(), "POST": request.POST.urlencode(), "method": request.method, "scheme": request.scheme, } ) def deserialize_request(s, request): data = json.loads(s) request.GET = QueryDict(data["GET"]) request.POST = QueryDict(data["POST"]) request.META = data["META"] request.path = data["path"] request.path_info = data["path_info"] request.method = data["method"] request._get_scheme = lambda: data["scheme"] return request def redirect(to): try: return shortcuts.redirect(to) except NoReverseMatch: return shortcuts.redirect(f"/{to}") def del_query_params(url: str, *params: str) -> str: parsed_url = urlparse(url) query_params = parse_qs(parsed_url.query, keep_blank_values=True) for param in params: query_params.pop(param, None) encoded_query = urlencode(query_params, doseq=True) new_url = urlunparse( ( parsed_url.scheme, parsed_url.netloc, parsed_url.path, parsed_url.params, encoded_query, parsed_url.fragment, ) ) return new_url def add_query_params(url: str, params: dict) -> str: parsed_url = urlparse(url) query_params = parse_qs(parsed_url.query) query_params.update(params) encoded_query = urlencode(query_params, doseq=True) new_url = urlunparse( ( parsed_url.scheme, parsed_url.netloc, parsed_url.path, parsed_url.params, encoded_query, parsed_url.fragment, ) ) return new_url def render_url(request, url_template, **kwargs): url = url_template for k, v in kwargs.items(): qi = url.find("?") ki = url.find(f"{{{k}}}") if ki < 0: raise ImproperlyConfigured(url_template) is_query_param = qi >= 0 and ki > qi if is_query_param: qv = urlencode({"k": v}).partition("k=")[2] else: qv = quote(v) url = url.replace(f"{{{k}}}", qv) p = urlparse(url) if not p.netloc: url = request.build_absolute_uri(url) return url def default_get_frontend_url(request, urlname, **kwargs): from allauth import app_settings as allauth_settings if allauth_settings.HEADLESS_ENABLED: from allauth.headless import app_settings as headless_settings url = headless_settings.FRONTEND_URLS.get(urlname) if allauth_settings.HEADLESS_ONLY and not url: raise ImproperlyConfigured(f"settings.HEADLESS_FRONTEND_URLS['{urlname}']") if url: return render_url(request, url, **kwargs) return None def get_frontend_url(request, urlname, **kwargs): from allauth import app_settings as allauth_settings if allauth_settings.HEADLESS_ENABLED: from allauth.headless.adapter import get_adapter return get_adapter().get_frontend_url(urlname, **kwargs) return default_get_frontend_url(request, urlname, **kwargs) def headed_redirect_response(viewname, query=None): """ In some cases, we're redirecting to a non-headless view. In case of headless-only mode, that view clearly does not exist. """ try: url = reverse(viewname) if query: url = add_query_params(url, query) return HttpResponseRedirect(url) except NoReverseMatch: if allauth_settings.HEADLESS_ONLY: # The response we would be rendering here is not actually used. return HttpResponseServerError() raise def is_headless_request(request: HttpRequest) -> str | None: """ Returns the headless client type (app/browser)in case of a headless request. """ return getattr( getattr(getattr(request, "allauth", None), "headless", None), "client", None ) def get_authorization_credential(request: HttpRequest, auth_scheme: str) -> str | None: auth = request.META.get("HTTP_AUTHORIZATION") if not auth: return None parts = auth.split() if not parts or len(parts) != 2 or parts[0].lower() != auth_scheme.lower(): return None return parts[1] def clean_client_ip(ip: str) -> str | None: """ Try to parse the value as an IP address to make sure it's a valid one. """ ip = ip.strip() try: domain, port = split_domain_port(ip) if port and domain: ip = domain # If Django splits off the port of an IPv6 address, the domain # has brackets. if ip[0] == "[" and ip[-1] == "]": ip = ip[1:-1] ip = str(ipaddress.ip_address(ip)) except ValueError: return None else: return ip def get_client_ip_from_xff(request: HttpRequest) -> str | None: trusted_proxy_count = allauth_settings.TRUSTED_PROXY_COUNT xff = request.headers.get("x-forwarded-for") if trusted_proxy_count > 0 and xff: ips = xff.split(",") if len(ips) < trusted_proxy_count: raise ImproperlyConfigured( "ALLAUTH_TRUSTED_PROXY_COUNT does not match X-Forwarded-For" ) ip = ips[-trusted_proxy_count] else: ip = None return ip def get_client_ip(request: HttpRequest) -> str | None: trusted_client_ip_header = allauth_settings.TRUSTED_CLIENT_IP_HEADER if trusted_client_ip_header: ip = request.headers.get(trusted_client_ip_header) else: ip = get_client_ip_from_xff(request) if not ip: ip = request.META["REMOTE_ADDR"] return clean_client_ip(ip) if ip else None ================================================ FILE: allauth/core/internal/jwkkit.py ================================================ # https://github.com/jpadilla/pyjwt/issues/1032 import base64 import hashlib import json import jwt from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey from cryptography.hazmat.primitives.serialization import load_pem_private_key from jwt.algorithms import RSAAlgorithm # In lexicographic order as described in RFC7638 JWK_REQUIRED_MEMBERS = { "EC": ("crv", "kty", "x", "y"), "RSA": ("e", "kty", "n"), "oct": ("k", "kty"), } def jwk_thumbprint(jwk_dict: dict) -> str: key_type = jwk_dict["kty"] members = JWK_REQUIRED_MEMBERS.get(key_type) if members is None: raise jwt.exceptions.PyJWKError(f"Unable to canonicalize key type {key_type}") json_bytes = json.dumps( {k: jwk_dict[k] for k in members}, separators=(",", ":"), ).encode() json_hash = hashlib.sha256(json_bytes).digest() return base64.urlsafe_b64encode(json_hash).rstrip(b"=").decode() def load_pem(pem: str) -> RSAPrivateKey: private_key = load_pem_private_key( pem.encode("utf8"), password=None, ) if not isinstance(private_key, RSAPrivateKey): raise ValueError return private_key def load_jwk_from_pem(pem: str) -> tuple[dict, RSAPrivateKey]: private_key = load_pem(pem) public_key = private_key.public_key() jwk_dict = json.loads(RSAAlgorithm.to_jwk(public_key)) jwk_dict["kid"] = jwk_thumbprint(jwk_dict) return jwk_dict, private_key ================================================ FILE: allauth/core/internal/modelkit.py ================================================ import base64 import json from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured from django.core.files.base import ContentFile from django.core.serializers.json import DjangoJSONEncoder from django.db.models import FileField from django.db.models.fields import BinaryField, DateField, DateTimeField, TimeField from django.utils import dateparse from django.utils.encoding import force_bytes, force_str SERIALIZED_DB_FIELD_PREFIX = "_db_" def serialize_instance(instance) -> dict: """ Since Django 1.6 items added to the session are no longer pickled, but JSON encoded by default. We are storing partially complete models in the session (user, account, token, ...). We cannot use standard Django serialization, as these are models are not "complete" yet. Serialization will start complaining about missing relations et al. """ data = {} for k, v in instance.__dict__.items(): if k.startswith("_") or callable(v): continue try: field = instance._meta.get_field(k) if isinstance(field, BinaryField): if v is not None: v = force_str(base64.b64encode(v)) elif isinstance(field, FileField): if v and not isinstance(v, str): v = { "name": v.name, "content": base64.b64encode(v.read()).decode("ascii"), } # Check if the field is serializable. If not, we'll fall back # to serializing the DB values which should cover most use cases. try: json.dumps(v, cls=DjangoJSONEncoder) except TypeError: v = field.get_prep_value(v) k = SERIALIZED_DB_FIELD_PREFIX + k except FieldDoesNotExist: pass data[k] = v return json.loads(json.dumps(data, cls=DjangoJSONEncoder)) def deserialize_instance(model, data): if not isinstance(data, dict): raise ValueError() ret = model() for k, v in data.items(): is_db_value = False if k.startswith(SERIALIZED_DB_FIELD_PREFIX): k = k[len(SERIALIZED_DB_FIELD_PREFIX) :] is_db_value = True if v is not None: try: f = model._meta.get_field(k) if isinstance(f, DateTimeField): v = dateparse.parse_datetime(v) elif isinstance(f, TimeField): v = dateparse.parse_time(v) elif isinstance(f, DateField): v = dateparse.parse_date(v) elif isinstance(f, BinaryField): v = force_bytes(base64.b64decode(force_bytes(v))) elif isinstance(f, FileField): if isinstance(v, dict): v = ContentFile(base64.b64decode(v["content"]), name=v["name"]) elif is_db_value: try: # This is quite an ugly hack, but will cover most # use cases... # The signature of `from_db_value` changed in Django 3 # https://docs.djangoproject.com/en/3.0/releases/3.0/#features-removed-in-3-0 v = f.from_db_value(v, None, None) except Exception: raise ImproperlyConfigured( f"Unable to auto serialize field '{k}', custom serialization override required" ) except FieldDoesNotExist: pass setattr(ret, k, v) return ret ================================================ FILE: allauth/core/internal/ratelimit.py ================================================ """ Rate limiting in this implementation relies on a cache and uses non-atomic operations, making it vulnerable to race conditions. As a result, users may occasionally bypass the intended rate limit due to concurrent access. However, such race conditions are rare in practice. For example, if the limit is set to 10 requests per minute and a large number of parallel processes attempt to test that limit, you may occasionally observe slight overruns—such as 11 or 12 requests slipping through. Nevertheless, exceeding the limit by a large margin is highly unlikely due to the low probability of many processes entering the critical non-atomic code section simultaneously. """ import hashlib import time from collections import namedtuple from dataclasses import dataclass from http import HTTPStatus from django.core.cache import cache from django.core.exceptions import ImproperlyConfigured from django.http import HttpRequest, HttpResponse from django.shortcuts import render from django.template.exceptions import TemplateDoesNotExist from allauth.core.exceptions import RateLimited Rate = namedtuple("Rate", "amount duration per") @dataclass class SingleRateLimitUsage: cache_key: str cache_duration: float | int timestamp: float def rollback(self) -> None: history = cache.get(self.cache_key, []) history = [ts for ts in history if ts != self.timestamp] cache.set(self.cache_key, history, self.cache_duration) @dataclass class RateLimitUsage: usage: list[SingleRateLimitUsage] def rollback(self) -> None: for usage in self.usage: usage.rollback() def parse_duration(duration) -> int | float: if len(duration) == 0: raise ValueError(duration) unit = duration[-1] value = duration[0:-1] unit_map = {"s": 1, "m": 60, "h": 3600, "d": 86400} if unit not in unit_map: raise ValueError(f"Invalid duration unit: {unit}") if len(value) == 0: value = 1 else: value = float(value) return value * unit_map[unit] def parse_rate(rate: str) -> Rate: parts = rate.split("/") if len(parts) == 2: amount, duration = parts per = "ip" elif len(parts) == 3: amount, duration, per = parts else: raise ValueError(rate) amount_v = int(amount) duration_v = parse_duration(duration) return Rate(amount_v, duration_v, per) def parse_rates(rates: str | None) -> list[Rate]: ret = [] if rates: rates = rates.strip() if rates: parts = rates.split(",") for part in parts: ret.append(parse_rate(part.strip())) return ret def get_cache_key(request, *, action: str, rate: Rate, key=None, user=None) -> str: from allauth.account.adapter import get_adapter source: tuple[str, ...] if rate.per == "ip": source = ("ip", get_adapter().get_client_ip(request)) elif rate.per == "user": if user is None: if not request.user.is_authenticated: raise ImproperlyConfigured( "ratelimit configured per user but used anonymously" ) user = request.user source = ("user", str(user.pk)) elif rate.per == "key": if key is None: raise ImproperlyConfigured( "ratelimit configured per key but no key specified" ) key_hash = hashlib.sha256(key.encode("utf8")).hexdigest() source = (key_hash,) else: raise ValueError(rate.per) keys = ["allauth", "rl", action, *source] return ":".join(keys) def _consume_single_rate( request, *, action: str, rate: Rate, key=None, user=None, dry_run: bool = False, raise_exception: bool = False, ) -> SingleRateLimitUsage | None: cache_key = get_cache_key(request, action=action, rate=rate, key=key, user=user) history = cache.get(cache_key, []) now = time.time() while history and history[-1] <= now - rate.duration: history.pop() allowed = len(history) < rate.amount if allowed: usage = SingleRateLimitUsage( cache_key=cache_key, timestamp=now, cache_duration=rate.duration ) else: usage = None if allowed and not dry_run: history.insert(0, now) cache.set(cache_key, history, rate.duration) if not allowed and raise_exception: raise RateLimited return usage def consume( request: HttpRequest, *, action: str, config: dict[str, str], key=None, user=None, dry_run: bool = False, limit_get: bool = False, raise_exception: bool = False, ) -> RateLimitUsage | None: usage = RateLimitUsage(usage=[]) if (not limit_get) and request.method == "GET": return usage rates = parse_rates(config.get(action)) if not rates: return usage allowed = True for rate in rates: single_usage = _consume_single_rate( request, action=action, rate=rate, key=key, user=user, dry_run=dry_run, raise_exception=raise_exception, ) if not single_usage: allowed = False break usage.usage.append(single_usage) return usage if allowed else None def handler429(request) -> HttpResponse: from allauth.account import app_settings try: return render( request, f"429.{app_settings.TEMPLATE_EXTENSION}", status=HTTPStatus.TOO_MANY_REQUESTS, ) except TemplateDoesNotExist: content = """ Too Many Requests

429 Too Many Requests

You have sent too many requests. Please try again later.

""" return HttpResponse( content=content, content_type="text/html", status=HTTPStatus.TOO_MANY_REQUESTS, ) def clear(request, *, config: dict, action: str, key=None, user=None) -> None: rates = parse_rates(config.get(action)) for rate in rates: cache_key = get_cache_key(request, action=action, rate=rate, key=key, user=user) cache.delete(cache_key) ================================================ FILE: allauth/core/internal/sessionkit.py ================================================ from django.contrib.auth import get_user from django.contrib.sessions.backends.base import SessionBase from django.http import HttpRequest def get_session_user(session: SessionBase): request = HttpRequest() request.session = session user = get_user(request) if not user or user.is_anonymous: return None return user ================================================ FILE: allauth/core/internal/urlkit.py ================================================ from django.conf import settings from django.urls import resolve def script_aware_resolve(path: str): """ Django's reverse/resolve is asymmetric. reverse() adds `FORCE_SCRIPT_NAME`, yet, resolve() won't handle it. """ if settings.FORCE_SCRIPT_NAME and path.startswith(settings.FORCE_SCRIPT_NAME): path = path.removeprefix(settings.FORCE_SCRIPT_NAME) return resolve(path) ================================================ FILE: allauth/core/ratelimit.py ================================================ from django.conf import settings from django.http import HttpResponse from allauth import app_settings from allauth.core.exceptions import RateLimited # noqa from allauth.core.internal import ratelimit as _impl from allauth.core.internal.ratelimit import Rate # noqa from allauth.utils import import_callable def clear(request, *, action: str, key=None, user=None) -> None: from allauth.account import app_settings _impl.clear( request=request, config=app_settings.RATE_LIMITS, action=action, key=key, user=user, ) def consume( request, *, action, key=None, user=None, dry_run: bool = False, raise_exception: bool = False, ) -> bool: """ TODO: We ened to deprecate this module and keep rate limiting internal to allauth. This method is using `allauth.account.app_settings` as a hard-coded source of settings, which is bad for reusability elsewhere. """ from allauth.account import app_settings usage = _impl.consume( request=request, config=app_settings.RATE_LIMITS, action=action, key=key, user=user, dry_run=dry_run, raise_exception=raise_exception, ) if not usage: return False return True def respond_429(request) -> HttpResponse: if app_settings.HEADLESS_ENABLED and hasattr(request.allauth, "headless"): from allauth.headless.base.response import RateLimitResponse return RateLimitResponse(request) try: handler429 = import_callable(f"{settings.ROOT_URLCONF}.handler429") handler429 = import_callable(handler429) except (ImportError, AttributeError): handler429 = _impl.handler429 return handler429(request) def consume_or_429(request, *args, **kwargs) -> HttpResponse | None: if not consume(request, *args, **kwargs): return respond_429(request) return None ================================================ FILE: allauth/decorators.py ================================================ from functools import wraps from allauth.core import ratelimit def rate_limit(*, action, **rl_kwargs): def decorator(function): @wraps(function) def wrap(request, *args, **kwargs): resp = ratelimit.consume_or_429(request, action=action, **rl_kwargs) if not resp: resp = function(request, *args, **kwargs) return resp return wrap return decorator ================================================ FILE: allauth/exceptions.py ================================================ import warnings from allauth.core.exceptions import ImmediateHttpResponse __all__ = ["ImmediateHttpResponse"] warnings.warn("allauth.exceptions is deprecated, use allauth.core.exceptions") ================================================ FILE: allauth/headless/__init__.py ================================================ ================================================ FILE: allauth/headless/account/__init__.py ================================================ ================================================ FILE: allauth/headless/account/inputs.py ================================================ from django.core.exceptions import ValidationError from django.core.validators import validate_email from allauth.account import app_settings as account_settings from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.fields import PhoneField from allauth.account.forms import ( AddEmailForm, BaseSignupForm, ChangeEmailForm, ChangePhoneForm, ConfirmLoginCodeForm, ReauthenticateForm, RequestLoginCodeForm, ResetPasswordForm, UserTokenForm, VerifyPhoneForm, ) from allauth.account.internal import flows from allauth.account.models import EmailAddress, Login, get_emailconfirmation_model from allauth.core import context from allauth.core.internal.cryptokit import compare_user_code from allauth.headless.adapter import get_adapter from allauth.headless.internal.restkit import inputs class SignupInput(BaseSignupForm, inputs.Input): password = inputs.CharField() def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) password = account_settings.SIGNUP_FIELDS.get("password1") if not password: del self.fields["password"] else: self.fields["password"].required = password["required"] def clean_password(self): password = self.cleaned_data["password"] if password: return get_account_adapter().clean_password(password) return password class LoginInput(inputs.Input): username = inputs.CharField(required=False) email = inputs.EmailField(required=False) # NOTE: Always require E164, no need to use adapter.phone_form_field phone = PhoneField(required=False) password = inputs.CharField() def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field in ["username", "email", "phone"]: if field not in account_settings.LOGIN_METHODS: del self.fields[field] if len(account_settings.LOGIN_METHODS) == 1: self.fields[next(iter(account_settings.LOGIN_METHODS))].required = True def clean(self): cleaned_data = super().clean() if self.errors: return cleaned_data credentials = {} for login_method in account_settings.LOGIN_METHODS: value = cleaned_data.get(login_method) if value is not None and login_method in self.data.keys(): credentials[login_method] = value if len(credentials) != 1: raise get_account_adapter().validation_error("invalid_login") password = cleaned_data.get("password") if password: auth_method = next(iter(credentials.keys())) credentials["password"] = password user = get_account_adapter().authenticate(context.request, **credentials) if user: self.login = Login(user=user, email=credentials.get("email")) if flows.login.is_login_rate_limited(context.request, self.login): raise get_account_adapter().validation_error( "too_many_login_attempts" ) else: error_code = f"{auth_method.value}_password_mismatch" self.add_error( "password", get_account_adapter().validation_error(error_code) ) return cleaned_data class VerifyEmailInput(inputs.Input): key = inputs.CharField() def __init__(self, *args, **kwargs): self.process = kwargs.pop("process", None) super().__init__(*args, **kwargs) def clean_key(self): key = self.cleaned_data["key"] if self.process: if not compare_user_code(actual=key, expected=self.process.code): raise get_account_adapter().validation_error("incorrect_code") valid = True email_address = self.process.email_address else: model = get_emailconfirmation_model() verification = model.from_key(key) valid = verification and not verification.key_expired() if not valid: raise get_account_adapter().validation_error( "incorrect_code" if account_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED else "invalid_or_expired_key" ) email_address = verification.email_address self.verification = verification if valid and not email_address.can_set_verified(): raise get_account_adapter().validation_error("email_taken") return key class RequestPasswordResetInput(ResetPasswordForm, inputs.Input): pass class ResetPasswordKeyInput(inputs.Input): key = inputs.CharField() def __init__(self, *args, **kwargs): self.user = kwargs.pop("user", None) self.code = kwargs.pop("code", None) super().__init__(*args, **kwargs) def clean_key(self): if account_settings.PASSWORD_RESET_BY_CODE_ENABLED: return self._clean_key_code() else: return self._clean_key_link() def _clean_key_code(self): key = self.cleaned_data["key"] if not compare_user_code(actual=key, expected=self.code): raise get_account_adapter().validation_error("incorrect_code") return key def _clean_key_link(self): key = self.cleaned_data["key"] uidb36, _, subkey = key.partition("-") token_form = UserTokenForm(data={"uidb36": uidb36, "key": subkey}) if not token_form.is_valid(): raise get_account_adapter().validation_error("invalid_password_reset") self.user = token_form.reset_user return key class ResetPasswordInput(ResetPasswordKeyInput): password = inputs.CharField() def clean(self): cleaned_data = super().clean() password = self.cleaned_data.get("password") if self.user and password is not None: try: get_account_adapter().clean_password(password, user=self.user) except ValidationError as e: self.add_error("password", e) return cleaned_data class ChangePasswordInput(inputs.Input): current_password = inputs.CharField(required=False) new_password = inputs.CharField() def __init__(self, *args, **kwargs): self.user = kwargs.pop("user") super().__init__(*args, **kwargs) self.fields["current_password"].required = self.user.has_usable_password() def clean_current_password(self): current_password = self.cleaned_data["current_password"] if current_password: if not self.user.check_password(current_password): raise get_account_adapter().validation_error("enter_current_password") return current_password def clean_new_password(self): new_password = self.cleaned_data["new_password"] adapter = get_account_adapter() return adapter.clean_password(new_password, user=self.user) class AddEmailInput(AddEmailForm, inputs.Input): pass class SelectEmailInput(inputs.Input): email = inputs.CharField() def __init__(self, *args, **kwargs): self.user = kwargs.pop("user") super().__init__(*args, **kwargs) def clean_email(self): self.process = None email = self.cleaned_data["email"] validate_email(email) # Select a database backed email... try: return EmailAddress.objects.get_for_user(user=self.user, email=email) except EmailAddress.DoesNotExist: pass # Or, if email verification by code is active, try the pending email request = context.request self.process = flows.email_verification_by_code.EmailVerificationProcess.resume( request ) if self.process: email_address = self.process.email_address if email_address.email.lower() == email.lower() and ( request.user.is_anonymous or email_address.user_id == request.user.pk ): return email_address raise get_adapter().validation_error("unknown_email") class DeleteEmailInput(SelectEmailInput): def clean_email(self): email = super().clean_email() if not flows.manage_email.can_delete_email(email): raise get_account_adapter().validation_error("cannot_remove_primary_email") return email class MarkAsPrimaryEmailInput(SelectEmailInput): primary = inputs.BooleanField(required=True) def clean_email(self): email = super().clean_email() if not flows.manage_email.can_mark_as_primary(email): raise get_account_adapter().validation_error("unverified_primary_email") return email class ResendEmailVerificationInput(SelectEmailInput): pass class ReauthenticateInput(ReauthenticateForm, inputs.Input): pass class RequestLoginCodeInput(RequestLoginCodeForm, inputs.Input): pass class ConfirmLoginCodeInput(ConfirmLoginCodeForm, inputs.Input): pass class VerifyPhoneInput(VerifyPhoneForm, inputs.Input): pass class ChangePhoneInput(ChangePhoneForm, inputs.Input): pass class ChangeEmailInput(ChangeEmailForm, inputs.Input): pass ================================================ FILE: allauth/headless/account/response.py ================================================ from http import HTTPStatus from allauth.headless.adapter import get_adapter from allauth.headless.base.response import APIResponse def email_address_data(addr) -> dict: return { "email": addr.email, "verified": addr.verified, "primary": addr.primary, } class RequestEmailVerificationResponse(APIResponse): def __init__(self, request, verification_sent): super().__init__( request, status=HTTPStatus.OK if verification_sent else HTTPStatus.FORBIDDEN ) class VerifyEmailResponse(APIResponse): def __init__(self, request, email_address, stage): adapter = get_adapter() data = { "email": email_address.email, "user": adapter.serialize_user(email_address.user), } meta = { "is_authenticating": stage is not None, } super().__init__(request, data=data, meta=meta) class EmailAddressesResponse(APIResponse): def __init__(self, request, email_addresses): data = [email_address_data(addr) for addr in email_addresses] super().__init__(request, data=data) class PhoneNumbersResponse(APIResponse): def __init__(self, request, phone_numbers, status=HTTPStatus.OK): super().__init__(request, data=phone_numbers, status=status) class RequestPasswordResponse(APIResponse): pass class PasswordResetKeyResponse(APIResponse): def __init__(self, request, user): adapter = get_adapter() data = {"user": adapter.serialize_user(user)} super().__init__(request, data=data) ================================================ FILE: allauth/headless/account/urls.py ================================================ from django.urls import include, path from allauth import app_settings as allauth_settings from allauth.account import app_settings as account_settings from allauth.headless.account import views def build_urlpatterns(client): account_patterns = [] auth_patterns = [ path( "session", views.SessionView.as_api_view(client=client), name="current_session", ), path( "reauthenticate", views.ReauthenticateView.as_api_view(client=client), name="reauthenticate", ), path( "code/confirm", views.ConfirmLoginCodeView.as_api_view(client=client), name="confirm_login_code", ), ] if not allauth_settings.SOCIALACCOUNT_ONLY: account_patterns.extend( [ path( "password/change", views.ChangePasswordView.as_api_view(client=client), name="change_password", ), path( "email", views.ManageEmailView.as_api_view(client=client), name="manage_email", ), path( "phone", views.ManagePhoneView.as_api_view(client=client), name="manage_phone", ), ] ) auth_patterns.extend( [ path( "password/", include( [ path( "request", views.RequestPasswordResetView.as_api_view( client=client ), name="request_password_reset", ), path( "reset", views.ResetPasswordView.as_api_view(client=client), name="reset_password", ), ] ), ), path( "login", views.LoginView.as_api_view(client=client), name="login", ), path( "signup", views.SignupView.as_api_view(client=client), name="signup", ), path( "email/verify", views.VerifyEmailView.as_api_view(client=client), name="verify_email", ), path( "email/verify/resend", views.ResendEmailVerificationCodeView.as_api_view(client=client), name="resend_email_verification_code", ), path( "phone/verify", views.VerifyPhoneView.as_api_view(client=client), name="verify_phone", ), path( "phone/verify/resend", views.ResendPhoneVerificationCodeView.as_api_view(client=client), name="resend_phone_verification_code", ), ] ) if account_settings.LOGIN_BY_CODE_ENABLED: auth_patterns.extend( [ path( "code/request", views.RequestLoginCodeView.as_api_view(client=client), name="request_login_code", ), path( "code/resend", views.ResendLoginCodeView.as_api_view(client=client), name="resend_login_code", ), ] ) return [ path("auth/", include(auth_patterns)), path( "account/", include(account_patterns), ), ] ================================================ FILE: allauth/headless/account/views.py ================================================ from http import HTTPStatus from django.http import HttpResponse from django.utils.decorators import method_decorator from allauth.account import app_settings as account_settings from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.internal import flows from allauth.account.internal.constants import LoginStageKey from allauth.account.internal.flows import ( email_verification, manage_email, password_change, password_reset, password_reset_by_code, ) from allauth.account.internal.flows.email_verification import ( send_verification_email_to_address, ) from allauth.account.internal.flows.email_verification_by_code import ( EmailVerificationProcess, ) from allauth.account.internal.flows.phone_verification import ( PhoneVerificationStageProcess, ) from allauth.account.stages import ( EmailVerificationStage, LoginStageController, PhoneVerificationStage, ) from allauth.core import ratelimit from allauth.core.exceptions import ImmediateHttpResponse, RateLimited from allauth.decorators import rate_limit from allauth.headless.account import response from allauth.headless.account.inputs import ( AddEmailInput, ChangeEmailInput, ChangePasswordInput, ChangePhoneInput, ConfirmLoginCodeInput, DeleteEmailInput, LoginInput, MarkAsPrimaryEmailInput, ReauthenticateInput, RequestLoginCodeInput, RequestPasswordResetInput, ResendEmailVerificationInput, ResetPasswordInput, ResetPasswordKeyInput, SignupInput, VerifyEmailInput, VerifyPhoneInput, ) from allauth.headless.base.response import ( APIResponse, AuthenticationResponse, ConflictResponse, ForbiddenResponse, RateLimitResponse, ) from allauth.headless.base.views import APIView, AuthenticatedAPIView from allauth.headless.internal import authkit from allauth.headless.internal.restkit.response import ErrorResponse class RequestLoginCodeView(APIView): input_class = RequestLoginCodeInput def post(self, request, *args, **kwargs): flows.login_by_code.LoginCodeVerificationProcess.initiate( request=self.request, user=self.input._user, email=self.input.cleaned_data.get("email"), phone=self.input.cleaned_data.get("phone"), ) return AuthenticationResponse(self.request) class ResendLoginCodeView(APIView): handle_json_input = False def post(self, request, *args, **kwargs): process = None stage = LoginStageController.enter(request, LoginStageKey.LOGIN_BY_CODE) if stage: process = flows.login_by_code.LoginCodeVerificationProcess.resume(stage) if not process or not process.can_resend: return ConflictResponse(request) try: process.resend() except RateLimited: return RateLimitResponse(request) return APIResponse(request) class ConfirmLoginCodeView(APIView): input_class = ConfirmLoginCodeInput def dispatch(self, request, *args, **kwargs): auth_status = authkit.AuthenticationStatus(request) self.stage = auth_status.get_pending_stage() if not self.stage or self.stage.key != LoginStageKey.LOGIN_BY_CODE: return ConflictResponse(request) self.process = flows.login_by_code.LoginCodeVerificationProcess.resume( self.stage ) if not self.process: return ConflictResponse(request) return super().dispatch(request, *args, **kwargs) def post(self, request, *args, **kwargs): response = self.process.finish(None) return AuthenticationResponse.from_response(request, response) def get_input_kwargs(self) -> dict: kwargs = super().get_input_kwargs() kwargs["code"] = self.process.code return kwargs def handle_invalid_input(self, input): self.process.record_invalid_attempt() return super().handle_invalid_input(input) @method_decorator(rate_limit(action="login"), name="handle") class LoginView(APIView): input_class = LoginInput def post(self, request, *args, **kwargs): if request.user.is_authenticated: return ConflictResponse(request) credentials = self.input.cleaned_data response = flows.login.perform_password_login( request, credentials, self.input.login ) return AuthenticationResponse.from_response(request, response) @method_decorator(rate_limit(action="signup"), name="handle") class SignupView(APIView): input_class = {"POST": SignupInput} by_passkey = False def post(self, request, *args, **kwargs): if request.user.is_authenticated: return ConflictResponse(request) if not get_account_adapter().is_open_for_signup(request): return ForbiddenResponse(request) user, resp = self.input.try_save(request) if not resp: try: resp = flows.signup.complete_signup( request, user=user, by_passkey=self.by_passkey ) except ImmediateHttpResponse: pass return AuthenticationResponse.from_response(request, resp) class SessionView(APIView): def get(self, request, *args, **kwargs) -> HttpResponse: return AuthenticationResponse(request) def delete(self, request, *args, **kwargs): adapter = get_account_adapter() adapter.logout(request) return AuthenticationResponse(request) class VerifyEmailView(APIView): input_class = VerifyEmailInput def handle(self, request, *args, **kwargs): self.stage = LoginStageController.enter(request, EmailVerificationStage.key) if ( not self.stage and account_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED and not request.user.is_authenticated ): return ConflictResponse(request) self.process = None if account_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED: self.process = ( flows.email_verification_by_code.EmailVerificationProcess.resume( request ) ) if not self.process: return ConflictResponse(request) return super().handle(request, *args, **kwargs) def get_input_kwargs(self) -> dict: return {"process": self.process} def handle_invalid_input(self, input: VerifyEmailInput): if self.process: self.process.record_invalid_attempt() return super().handle_invalid_input(input) def get(self, request, *args, **kwargs) -> HttpResponse: key = request.headers.get("x-email-verification-key", "") input = self.input_class({"key": key}, process=self.process) if not input.is_valid(): if self.process: self.process.record_invalid_attempt() return ErrorResponse(request, input=input) if self.process: email_address = self.process.email_address else: email_address = input.verification.email_address return response.VerifyEmailResponse(request, email_address, stage=self.stage) def post(self, request, *args, **kwargs): if self.process: email_address = self.process.finish() else: email_address = self.input.verification.confirm(request) if not email_address: # Should not happen, VerifyInputInput should have verified all # preconditions. return APIResponse(request, status=HTTPStatus.INTERNAL_SERVER_ERROR) response = None if self.stage: # Verifying email as part of login/signup flow may imply the user is # to be logged in... response = email_verification.login_on_verification(request, email_address) return AuthenticationResponse.from_response(request, response) class VerifyPhoneView(APIView): input_class = VerifyPhoneInput def handle(self, request, *args, **kwargs): self.stage = LoginStageController.enter(request, PhoneVerificationStage.key) if self.stage: self.process = ( flows.phone_verification.PhoneVerificationStageProcess.resume( self.stage ) ) else: if not request.user.is_authenticated: return ConflictResponse(request) self.process = ( flows.phone_verification.ChangePhoneVerificationProcess.resume(request) ) if not self.process: return ConflictResponse(request) return super().handle(request, *args, **kwargs) def get_input_kwargs(self) -> dict: return { "code": self.process.code, "user": self.process.user, "phone": self.process.phone, } def handle_invalid_input(self, input: VerifyPhoneInput): self.process.record_invalid_attempt() return super().handle_invalid_input(input) def post(self, request, *args, **kwargs): self.process.finish() response = None if self.stage: response = self.stage.exit() return AuthenticationResponse.from_response(request, response) class ResendPhoneVerificationCodeView(APIView): handle_json_input = False def post(self, request, *args, **kwargs): process = None stage = LoginStageController.enter(request, PhoneVerificationStage.key) if stage: process = flows.phone_verification.PhoneVerificationStageProcess.resume( stage ) if not process or not process.can_resend: return ConflictResponse(request) try: process.resend() except RateLimited: return RateLimitResponse(request) return APIResponse(request) class ResendEmailVerificationCodeView(APIView): handle_json_input = False def post(self, request, *args, **kwargs): if not account_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED: return ConflictResponse(request) process = flows.email_verification_by_code.EmailVerificationProcess.resume( request ) if not process or not process.can_resend: return ConflictResponse(request) try: process.resend() except RateLimited: return RateLimitResponse(request) return APIResponse(request) class RequestPasswordResetView(APIView): input_class = RequestPasswordResetInput def post(self, request, *args, **kwargs): r429 = ratelimit.consume_or_429( self.request, action="reset_password", key=self.input.cleaned_data["email"].lower(), ) if r429: return r429 self.input.save(request) if account_settings.PASSWORD_RESET_BY_CODE_ENABLED: return AuthenticationResponse(request) return response.RequestPasswordResponse(request) @method_decorator(rate_limit(action="reset_password_from_key"), name="handle") class ResetPasswordView(APIView): input_class = ResetPasswordInput def handle_invalid_input(self, input: ResetPasswordInput): if self.process and "key" in input.errors: self.process.record_invalid_attempt() return super().handle_invalid_input(input) def handle(self, request, *args, **kwargs): self.process = None if account_settings.PASSWORD_RESET_BY_CODE_ENABLED: self.process = ( password_reset_by_code.PasswordResetVerificationProcess.resume( self.request ) ) if not self.process: return ConflictResponse(request) return super().handle(request, *args, **kwargs) def get(self, request, *args, **kwargs) -> HttpResponse: key = request.headers.get("X-Password-Reset-Key", "") if self.process: input = ResetPasswordKeyInput({"key": key}, code=self.process.code) if not input.is_valid(): self.process.record_invalid_attempt() return ErrorResponse(request, input=input) self.process.confirm_code() return response.PasswordResetKeyResponse(request, self.process.user) else: input = ResetPasswordKeyInput({"key": key}) if not input.is_valid(): return ErrorResponse(request, input=input) return response.PasswordResetKeyResponse(request, input.user) def get_input_kwargs(self) -> dict: ret = {} if self.process: ret.update({"code": self.process.code, "user": self.process.user}) return ret def post(self, request, *args, **kwargs): user = self.input.user flows.password_reset.reset_password(user, self.input.cleaned_data["password"]) if self.process: self.process.confirm_code() self.process.finish() else: password_reset.finalize_password_reset(request, user) return AuthenticationResponse(self.request) @method_decorator(rate_limit(action="change_password"), name="handle") class ChangePasswordView(AuthenticatedAPIView): input_class = ChangePasswordInput def post(self, request, *args, **kwargs): password_change.change_password( self.request.user, self.input.cleaned_data["new_password"] ) is_set = not self.input.cleaned_data.get("current_password") if is_set: password_change.finalize_password_set(request, request.user) else: password_change.finalize_password_change(request, request.user) return AuthenticationResponse(request) def get_input_kwargs(self) -> dict: return {"user": self.request.user} class ManageEmailView(APIView): input_class = { "POST": AddEmailInput, "PUT": ResendEmailVerificationInput, "DELETE": DeleteEmailInput, "PATCH": MarkAsPrimaryEmailInput, } def dispatch(self, request, *args, **kwargs): self.verification_stage_process = None if request.user.is_authenticated: self.user = request.user elif request.method != "POST": return AuthenticationResponse(request) else: self.verification_stage_process = EmailVerificationProcess.resume(request) if ( not self.verification_stage_process or not self.verification_stage_process.can_change ): return ConflictResponse(request) self.user = self.verification_stage_process.user resp = ratelimit.consume_or_429(request, action="manage_email", user=self.user) if resp: return resp return super().dispatch(request, *args, **kwargs) def get(self, request, *args, **kwargs) -> HttpResponse: return self._respond_email_list() def _respond_email_list(self): addrs = manage_email.list_email_addresses(self.request, self.user) return response.EmailAddressesResponse(self.request, addrs) def post(self, request, *args, **kwargs): if self.verification_stage_process: self.verification_stage_process.change_to( email=self.input.cleaned_data["email"], account_already_exists=self.input.account_already_exists, ) else: flows.manage_email.add_email(request, self.input) return self._respond_email_list() def delete(self, request, *args, **kwargs): addr = self.input.cleaned_data["email"] if addr.pk: flows.manage_email.delete_email(request, addr) else: self.input.process.abort() return self._respond_email_list() def patch(self, request, *args, **kwargs): addr = self.input.cleaned_data["email"] flows.manage_email.mark_as_primary(request, addr) return self._respond_email_list() def put(self, request, *args, **kwargs): addr = self.input.cleaned_data["email"] if process := self.input.process: sent = False if process.can_resend: try: self.input.process.resend() sent = True except RateLimited: pass else: sent = send_verification_email_to_address(request, addr) return response.RequestEmailVerificationResponse( request, verification_sent=sent ) def get_input_class(self): if self.verification_stage_process: return ChangeEmailInput return super().get_input_class() def get_input_kwargs(self) -> dict: if self.verification_stage_process: return {"email": self.verification_stage_process.email} return {"user": self.user} class ManagePhoneView(APIView): input_class = ChangePhoneInput def dispatch(self, request, *args, **kwargs): self.verification_stage_process = None if request.user.is_authenticated: self.user = request.user elif request.method == "GET": return AuthenticationResponse(request) elif request.method == "POST": stage = LoginStageController.enter(request, PhoneVerificationStage.key) if stage: self.verification_stage_process = PhoneVerificationStageProcess.resume( stage ) if ( not self.verification_stage_process or not self.verification_stage_process.can_change ): return ConflictResponse(request) self.user = self.verification_stage_process.user resp = ratelimit.consume_or_429(request, action="change_phone", user=self.user) if resp: return resp return super().dispatch(request, *args, **kwargs) def get(self, request, *args, **kwargs) -> HttpResponse: phone_numbers = [] phone_verified = get_account_adapter().get_phone(self.request.user) if phone_verified: phone_numbers.append( {"phone": phone_verified[0], "verified": phone_verified[1]} ) return response.PhoneNumbersResponse(self.request, phone_numbers) def post(self, request, *args, **kwargs): phone = self.input.cleaned_data["phone"] if self.verification_stage_process: self.verification_stage_process.change_to( phone=phone, account_already_exists=self.input.account_already_exists ) else: flows.phone_verification.ChangePhoneVerificationProcess.initiate( self.request, phone, ) return response.PhoneNumbersResponse( self.request, [ { "phone": phone, "verified": False, } ], status=HTTPStatus.ACCEPTED, ) def get_input_kwargs(self) -> dict: phone = None phone_verified = get_account_adapter().get_phone(self.user) if phone_verified: phone = phone_verified[0] return {"phone": phone, "user": self.user} @method_decorator(rate_limit(action="reauthenticate"), name="handle") class ReauthenticateView(AuthenticatedAPIView): input_class = ReauthenticateInput def post(self, request, *args, **kwargs): flows.reauthentication.reauthenticate_by_password(self.request) return AuthenticationResponse(self.request) def get_input_kwargs(self) -> dict: return {"user": self.request.user} ================================================ FILE: allauth/headless/adapter.py ================================================ import dataclasses import uuid from typing import Any, Optional from django.contrib.auth import get_user_model from django.db import models from django.forms.fields import Field from allauth.account import app_settings as account_settings from allauth.account.models import EmailAddress from allauth.account.utils import user_display, user_username from allauth.core.internal.adapter import BaseAdapter from allauth.core.internal.httpkit import default_get_frontend_url from allauth.headless import app_settings from allauth.utils import import_attribute class DefaultHeadlessAdapter(BaseAdapter): """The adapter class allows you to override various functionality of the ``allauth.headless`` app. To do so, point ``settings.HEADLESS_ADAPTER`` to your own class that derives from ``DefaultHeadlessAdapter`` and override the behavior by altering the implementation of the methods according to your own need. """ error_messages = { # For the following error messages i18n is not an issue as these should not be # showing up in a UI. "account_not_found": "Unknown account.", "client_id_required": "`client_id` required.", "invalid_token": "Invalid token.", # nosec "token_authentication_not_supported": "Provider does not support token authentication.", "token_required": "`id_token` and/or `access_token` required.", "required": Field.default_error_messages["required"], "unknown_email": "Unknown email address.", "unknown_provider": "Unknown provider.", "invalid_url": "Invalid URL.", } def serialize_user(self, user) -> dict[str, Any]: """ Returns the basic user data. Note that this data is also exposed in partly authenticated scenario's (e.g. password reset, email verification). Do not override this method if you would like your customized user payloads to be reflected in the (dynamically rendered) OpenAPI specification. In that case, override ``get_user_dataclass()`` and ``user_as_dataclass`` instead. """ return { k: v for k, v in dataclasses.asdict(self.user_as_dataclass(user)).items() if v not in ("", None) } def get_frontend_url(self, urlname: str, **kwargs) -> str | None: """Return the frontend URL for the given URL name.""" return default_get_frontend_url(self.request, urlname, **kwargs) def user_as_dataclass(self, user): """ See ``get_user_dataclass()``. This method returns an instance of that ``dataclass``, populated with the given ``user`` fields. """ UserDc = self.get_user_dataclass() kwargs = {} User = get_user_model() pk_field_class = type(User._meta.pk) if not user.pk: id_dc = None elif issubclass(pk_field_class, models.IntegerField): id_dc = user.pk else: id_dc = str(user.pk) if account_settings.USER_MODEL_USERNAME_FIELD: kwargs["username"] = user_username(user) if user.pk: email = EmailAddress.objects.get_primary_email(user) else: email = None kwargs.update( { "id": id_dc, "email": email if email else None, "display": user_display(user), "has_usable_password": user.has_usable_password(), } ) return UserDc(**kwargs) def get_user_dataclass(self): """ Basic user data payloads are exposed in some of the headless responses. If you need to customize these payloads in such a way that your custom user payload is also reflected in the OpenAPI specification, you wil need to provide a ``dataclass`` representing the schema of your custom payload, as well as method that takes a ``User`` instance and wraps it into your dataclass. This method returns that ``dataclass``. """ fields = [] User = get_user_model() pk_field_class = type(User._meta.pk) if issubclass(pk_field_class, models.UUIDField): id_type = str id_example = str(uuid.uuid4()) elif issubclass(pk_field_class, models.IntegerField): id_type = int id_example = 123 else: id_type = str id_example = "uid" def dc_field(attr, typ, description, example): return ( attr, typ, dataclasses.field( metadata={ "description": description, "example": example, } ), ) fields.extend( [ dc_field("id", Optional[id_type], "The user ID.", id_example), dc_field( "display", str, "The display name for the user.", "Magic Wizard" ), dc_field( "email", Optional[str], "The email address.", "email@domain.org" ), dc_field( "has_usable_password", bool, "Whether or not the account has a password set.", True, ), ] ) if account_settings.USER_MODEL_USERNAME_FIELD: fields.append(dc_field("username", str, "The username.", "wizard")) return dataclasses.make_dataclass("User", fields) def get_adapter() -> DefaultHeadlessAdapter: return import_attribute(app_settings.ADAPTER)() ================================================ FILE: allauth/headless/app_settings.py ================================================ class AppSettings: def __init__(self, prefix: str) -> None: self.prefix = prefix def _setting(self, name: str, dflt): from allauth.utils import get_setting return get_setting(self.prefix + name, dflt) @property def ADAPTER(self) -> str: return self._setting( "ADAPTER", "allauth.headless.adapter.DefaultHeadlessAdapter" ) @property def TOKEN_STRATEGY(self): from allauth.utils import import_attribute path = self._setting( "TOKEN_STRATEGY", "allauth.headless.tokens.strategies.sessions.SessionTokenStrategy", ) cls = import_attribute(path) return cls() @property def SERVE_SPECIFICATION(self) -> bool: return self._setting("SERVE_SPECIFICATION", False) @property def SPECIFICATION_TEMPLATE_NAME(self) -> str | None: return self._setting( "SPECIFICATION_TEMPLATE_NAME", "headless/spec/redoc_cdn.html" ) @property def CLIENTS(self) -> tuple[str]: return tuple(self._setting("CLIENTS", ("browser", "app"))) @property def FRONTEND_URLS(self) -> dict: return self._setting("FRONTEND_URLS", {}) @property def JWT_ALGORITHM(self) -> str: return self._setting("JWT_ALGORITHM", "RS256") @property def JWT_PRIVATE_KEY(self) -> str: return self._setting("JWT_PRIVATE_KEY", "") @property def JWT_ACCESS_TOKEN_EXPIRES_IN(self) -> int: return self._setting("JWT_ACCESS_TOKEN_EXPIRES_IN", 300) @property def JWT_REFRESH_TOKEN_EXPIRES_IN(self) -> int: return self._setting("JWT_REFRESH_TOKEN_EXPIRES_IN", 86400) @property def JWT_AUTHORIZATION_HEADER_SCHEME(self) -> str: return self._setting("JWT_AUTHORIZATION_HEADER_SCHEME", "Bearer") @property def JWT_STATEFUL_VALIDATION_ENABLED(self) -> bool: return self._setting("JWT_STATEFUL_VALIDATION_ENABLED", False) @property def JWT_ROTATE_REFRESH_TOKEN(self) -> bool: return self._setting("JWT_ROTATE_REFRESH_TOKEN", True) _app_settings = AppSettings("HEADLESS_") def __getattr__(name): # See https://peps.python.org/pep-0562/ return getattr(_app_settings, name) ================================================ FILE: allauth/headless/apps.py ================================================ from django.apps import AppConfig from django.utils.translation import gettext_lazy as _ class HeadlessConfig(AppConfig): name = "allauth.headless" verbose_name = _("Headless") def ready(self): from allauth.headless import checks # noqa ================================================ FILE: allauth/headless/base/__init__.py ================================================ ================================================ FILE: allauth/headless/base/response.py ================================================ from http import HTTPStatus from allauth import app_settings as allauth_settings from allauth.account import app_settings as account_settings from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.app_settings import LoginMethod from allauth.account.authentication import get_authentication_records from allauth.account.internal import flows from allauth.account.internal.stagekit import LOGIN_SESSION_KEY from allauth.headless.adapter import get_adapter from allauth.headless.constants import Flow from allauth.headless.internal import authkit from allauth.headless.internal.restkit.response import APIResponse from allauth.mfa import app_settings as mfa_settings class BaseAuthenticationResponse(APIResponse): def __init__(self, request, user=None, status=None): data = {} if user and user.is_authenticated: adapter = get_adapter() data["user"] = adapter.serialize_user(user) data["methods"] = get_authentication_records(request) status = status or HTTPStatus.OK else: status = status or HTTPStatus.UNAUTHORIZED if status != HTTPStatus.OK: data["flows"] = self._get_flows(request, user) meta = { "is_authenticated": user and user.is_authenticated, } super().__init__( request, data=data, meta=meta, status=status, ) def _get_flows(self, request, user): auth_status = authkit.AuthenticationStatus(request) ret = [] if user and user.is_authenticated: ret.extend(flows.reauthentication.get_reauthentication_flows(user)) else: if not allauth_settings.SOCIALACCOUNT_ONLY: ret.append({"id": Flow.LOGIN}) if account_settings.LOGIN_BY_CODE_ENABLED: ret.append({"id": Flow.LOGIN_BY_CODE}) if ( get_account_adapter().is_open_for_signup(request) and not allauth_settings.SOCIALACCOUNT_ONLY ): ret.append({"id": Flow.SIGNUP}) if allauth_settings.SOCIALACCOUNT_ENABLED: from allauth.headless.socialaccount.response import provider_flows ret.extend(provider_flows(request)) if allauth_settings.MFA_ENABLED: if mfa_settings.PASSKEY_LOGIN_ENABLED: ret.append({"id": Flow.MFA_LOGIN_WEBAUTHN}) stage_key = None stage = auth_status.get_pending_stage() if stage: stage_key = stage.key else: lsk = request.session.get(LOGIN_SESSION_KEY) if isinstance(lsk, str): stage_key = lsk if stage_key: pending_flow = {"id": stage_key, "is_pending": True} if stage and stage_key == Flow.MFA_AUTHENTICATE: self._enrich_mfa_flow(stage, pending_flow) self._upsert_pending_flow(ret, pending_flow) if ( not allauth_settings.SOCIALACCOUNT_ONLY and account_settings.PASSWORD_RESET_BY_CODE_ENABLED ): from allauth.account.internal.flows import password_reset_by_code ret.append( { "id": Flow.PASSWORD_RESET_BY_CODE, "is_pending": bool( password_reset_by_code.PasswordResetVerificationProcess.resume( request ) ), } ) return ret def _upsert_pending_flow(self, flows, pending_flow): flow = next((flow for flow in flows if flow["id"] == pending_flow["id"]), None) if flow: flow.update(pending_flow) else: flows.append(pending_flow) def _enrich_mfa_flow(self, stage, flow: dict) -> None: from allauth.mfa.adapter import get_adapter as get_mfa_adapter from allauth.mfa.models import Authenticator adapter = get_mfa_adapter() types = [] for typ in Authenticator.Type: if adapter.is_mfa_enabled(stage.login.user, types=[typ]): types.append(typ) flow["types"] = types class AuthenticationResponse(BaseAuthenticationResponse): def __init__(self, request): super().__init__(request, user=request.user) @classmethod def from_response(cls, request, response): """ The response might be a headed redirect to e.g. the confirmation email page, because allauth.account is not (much) headless aware. Also, what if an adapter method return headed responses in post_login()? So, let's ensure we always return a headless response. """ if isinstance(response, AuthenticationResponse): return response return AuthenticationResponse(request) class ReauthenticationResponse(BaseAuthenticationResponse): def __init__(self, request): super().__init__(request, user=request.user, status=HTTPStatus.UNAUTHORIZED) class UnauthorizedResponse(BaseAuthenticationResponse): def __init__(self, request, status=HTTPStatus.UNAUTHORIZED): super().__init__(request, user=None, status=status) class ForbiddenResponse(APIResponse): def __init__(self, request): super().__init__(request, status=HTTPStatus.FORBIDDEN) class ConflictResponse(APIResponse): def __init__(self, request): super().__init__(request, status=HTTPStatus.CONFLICT) def get_config_data(request) -> dict: login_methods = account_settings.LOGIN_METHODS data = { "login_methods": list(login_methods), "is_open_for_signup": get_account_adapter().is_open_for_signup(request), "email_verification_by_code_enabled": account_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED, "login_by_code_enabled": account_settings.LOGIN_BY_CODE_ENABLED, "password_reset_by_code_enabled": account_settings.PASSWORD_RESET_BY_CODE_ENABLED, } # NOTE: For backwards compatibility only. if LoginMethod.EMAIL in login_methods and LoginMethod.USERNAME in login_methods: data["authentication_method"] = "username_email" elif LoginMethod.EMAIL in login_methods: data["authentication_method"] = "email" elif LoginMethod.USERNAME in login_methods: data["authentication_method"] = "username" # (end NOTE) return {"account": data} class ConfigResponse(APIResponse): def __init__(self, request): data = get_config_data(request) if allauth_settings.SOCIALACCOUNT_ENABLED: from allauth.headless.socialaccount.response import ( get_config_data as get_socialaccount_config_data, ) data.update(get_socialaccount_config_data(request)) if allauth_settings.MFA_ENABLED: from allauth.headless.mfa.response import ( get_config_data as get_mfa_config_data, ) data.update(get_mfa_config_data(request)) if allauth_settings.USERSESSIONS_ENABLED: from allauth.headless.usersessions.response import ( get_config_data as get_usersessions_config_data, ) data.update(get_usersessions_config_data(request)) return super().__init__(request, data=data) class RateLimitResponse(APIResponse): def __init__(self, request): super().__init__(request, status=HTTPStatus.TOO_MANY_REQUESTS) ================================================ FILE: allauth/headless/base/urls.py ================================================ from django.urls import path from allauth.headless.base import views def build_urlpatterns(client): return [ path( "config", views.ConfigView.as_api_view(client=client), name="config", ), ] ================================================ FILE: allauth/headless/base/views.py ================================================ from django.utils.decorators import classonlymethod from allauth.account.stages import LoginStage, LoginStageController from allauth.core.exceptions import ReauthenticationRequired from allauth.headless.base import response from allauth.headless.constants import Client from allauth.headless.internal import decorators from allauth.headless.internal.restkit.views import RESTView class APIView(RESTView): client = None @classonlymethod def as_api_view(cls, **initkwargs): view_func = cls.as_view(**initkwargs) if initkwargs["client"] == Client.APP: view_func = decorators.app_view(view_func) else: view_func = decorators.browser_view(view_func) return view_func def dispatch(self, request, *args, **kwargs): try: return super().dispatch(request, *args, **kwargs) except ReauthenticationRequired: return response.ReauthenticationResponse(self.request) class AuthenticationStageAPIView(APIView): stage_class: type[LoginStage] | None = None def handle(self, request, *args, **kwargs): self.stage = LoginStageController.enter(request, self.stage_class.key) if not self.stage: return response.UnauthorizedResponse(request) return super().handle(request, *args, **kwargs) def respond_stage_error(self) -> response.UnauthorizedResponse: return response.UnauthorizedResponse(self.request) def respond_next_stage(self) -> response.AuthenticationResponse: self.stage.exit() return response.AuthenticationResponse(self.request) class AuthenticatedAPIView(APIView): def dispatch(self, request, *args, **kwargs): if not request.user.is_authenticated: return response.AuthenticationResponse(request) return super().dispatch(request, *args, **kwargs) class ConfigView(APIView): def get(self, request, *args, **kwargs): """ The frontend queries (GET) this endpoint, expecting to receive either a 401 if no user is authenticated, or user information. """ return response.ConfigResponse(request) ================================================ FILE: allauth/headless/checks.py ================================================ from django.core.checks import Critical, register @register() def settings_check(app_configs, **kwargs): from allauth.headless import app_settings ret = [] if app_settings.SERVE_SPECIFICATION: try: import yaml # noqa except ImportError: ret.append( Critical( msg="HEADLESS_SERVE_SPECIFICATION requires the django-allauth[headless-spec] extra to be installed" ) ) return ret ================================================ FILE: allauth/headless/constants.py ================================================ from enum import Enum from allauth.account.internal.constants import LoginStageKey as AccountLoginStageKey from allauth.mfa.internal.constants import LoginStageKey as MFALoginStageKey class Client(str, Enum): APP = "app" BROWSER = "browser" class Flow(str, Enum): LOGIN = "login" LOGIN_BY_CODE = AccountLoginStageKey.LOGIN_BY_CODE.value MFA_AUTHENTICATE = MFALoginStageKey.MFA_AUTHENTICATE.value MFA_LOGIN_WEBAUTHN = "mfa_login_webauthn" MFA_REAUTHENTICATE = "mfa_reauthenticate" MFA_SIGNUP_WEBAUTHN = MFALoginStageKey.MFA_SIGNUP_WEBAUTHN.value MFA_TRUST = MFALoginStageKey.MFA_TRUST.value PASSWORD_RESET_BY_CODE = "password_reset_by_code" # nosec PROVIDER_REDIRECT = "provider_redirect" PROVIDER_SIGNUP = "provider_signup" PROVIDER_TOKEN = "provider_token" # nosec REAUTHENTICATE = "reauthenticate" SIGNUP = "signup" VERIFY_EMAIL = AccountLoginStageKey.VERIFY_EMAIL.value VERIFY_PHONE = AccountLoginStageKey.VERIFY_PHONE.value ================================================ FILE: allauth/headless/contrib/__init__.py ================================================ ================================================ FILE: allauth/headless/contrib/ninja/__init__.py ================================================ ================================================ FILE: allauth/headless/contrib/ninja/security.py ================================================ from django.http import HttpRequest from ninja.security.base import AuthBase from allauth.core.internal.httpkit import get_authorization_credential from allauth.headless import app_settings from allauth.headless.internal.sessionkit import authenticate_by_x_session_token from allauth.headless.tokens.strategies.jwt.internal import validate_access_token class XSessionTokenAuth(AuthBase): """ This security class uses the X-Session-Token that django-allauth is using for authentication purposes. """ openapi_type: str = "apiKey" def __call__(self, request: HttpRequest): token = self.get_session_token(request) if token: user_session = authenticate_by_x_session_token(token) if user_session: return user_session[0] return None def get_session_token(self, request: HttpRequest) -> str | None: """ Returns the session token for the given request, by looking up the ``X-Session-Token`` header. Override this if you want to extract the token from e.g. the ``Authorization`` header. """ return request.headers.get("X-Session-Token") x_session_token_auth = XSessionTokenAuth() class JWTTokenAuth(AuthBase): openapi_type: str = "apiKey" def __call__(self, request: HttpRequest): token = get_authorization_credential( request, app_settings.JWT_AUTHORIZATION_HEADER_SCHEME ) if token is None: return None user_payload = validate_access_token(token) if user_payload is None: return None user, payload = user_payload request.user = user return payload jwt_token_auth = JWTTokenAuth() ================================================ FILE: allauth/headless/contrib/rest_framework/__init__.py ================================================ ================================================ FILE: allauth/headless/contrib/rest_framework/authentication.py ================================================ from django.http import HttpRequest from rest_framework import authentication from rest_framework.exceptions import AuthenticationFailed from allauth.headless import app_settings from allauth.headless.internal.sessionkit import authenticate_by_x_session_token class XSessionTokenAuthentication(authentication.BaseAuthentication): """ This authentication class uses the X-Session-Token that django-allauth is using for authentication purposes. """ def authenticate(self, request: HttpRequest): token = self.get_session_token(request) if token: return authenticate_by_x_session_token(token) return None def get_session_token(self, request: HttpRequest) -> str | None: """ Returns the session token for the given request, by looking up the ``X-Session-Token`` header. Override this if you want to extract the token from e.g. the ``Authorization`` header. """ return request.headers.get("X-Session-Token") class JWTTokenAuthentication(authentication.TokenAuthentication): @property def keyword(self) -> str: """ See: ``settings.HEADLESS_JWT_AUTHORIZATION_HEADER_SCHEME``. """ return app_settings.JWT_AUTHORIZATION_HEADER_SCHEME def authenticate_credentials(self, key: str): """ Validates the given access token. """ from allauth.headless.tokens.strategies.jwt.internal import ( validate_access_token, ) user_payload = validate_access_token(key) if user_payload is None: raise AuthenticationFailed("Invalid token.") return user_payload ================================================ FILE: allauth/headless/internal/__init__.py ================================================ ================================================ FILE: allauth/headless/internal/authkit.py ================================================ from contextlib import contextmanager from typing import Any from django.utils.functional import SimpleLazyObject, empty from allauth import app_settings as allauth_settings from allauth.account.internal.stagekit import get_pending_stage from allauth.core.exceptions import ImmediateHttpResponse from allauth.headless import app_settings from allauth.headless.constants import Client from allauth.headless.internal import sessionkit class AuthenticationStatus: def __init__(self, request) -> None: self.request = request @property def is_authenticated(self) -> bool: return self.request.user.is_authenticated def get_pending_stage(self): return get_pending_stage(self.request) @property def has_pending_signup(self) -> bool: if not allauth_settings.SOCIALACCOUNT_ENABLED: return False from allauth.socialaccount.internal import flows return bool(flows.signup.get_pending_signup(self.request)) def purge_request_user_cache(request) -> None: for attr in ["_cached_user", "_acached_user"]: if hasattr(request, attr): delattr(request, attr) if isinstance(request.user, SimpleLazyObject): request.user._wrapped = empty @contextmanager def authentication_context(request): from allauth.headless.base.response import UnauthorizedResponse old_user = request.user old_session = request.session try: request.session = sessionkit.new_session() purge_request_user_cache(request) strategy = app_settings.TOKEN_STRATEGY session_token = strategy.get_session_token(request) if session_token: session = strategy.lookup_session(session_token) if not session: raise ImmediateHttpResponse(UnauthorizedResponse(request, status=410)) request.session = session purge_request_user_cache(request) request.allauth.headless._pre_user = request.user # request.user is lazy -- force evaluation request.allauth.headless._pre_user.pk yield finally: if request.session.modified and not request.session.is_empty(): request.session.save() request.user = old_user request.session = old_session # e.g. logging in calls csrf `rotate_token()` -- this prevents setting a new CSRF cookie. request.META["CSRF_COOKIE_NEEDS_UPDATE"] = False def expose_access_token(request) -> dict[str, Any] | None: """ Determines if a new access token needs to be exposed. """ if request.allauth.headless.client != Client.APP: return None if not request.user.is_authenticated: return None pre_user = request.allauth.headless._pre_user if pre_user.is_authenticated and pre_user.pk == request.user.pk: return None strategy = app_settings.TOKEN_STRATEGY return strategy.create_access_token_payload(request) ================================================ FILE: allauth/headless/internal/decorators.py ================================================ from functools import wraps from types import SimpleNamespace from django.middleware.csrf import get_token from django.views.decorators.csrf import csrf_exempt from allauth.account.internal.decorators import login_not_required from allauth.headless.constants import Client from allauth.headless.internal import authkit def mark_request_as_headless(request, client) -> None: request.allauth.headless = SimpleNamespace() request.allauth.headless.client = client def app_view( function=None, ): def decorator(view_func): @login_not_required @wraps(view_func) def _wrapper_view(request, *args, **kwargs): mark_request_as_headless(request, Client.APP) with authkit.authentication_context(request): return view_func(request, *args, **kwargs) return _wrapper_view ret = decorator if function: ret = decorator(function) return csrf_exempt(ret) def browser_view( function=None, ): def decorator(view_func): @login_not_required @wraps(view_func) def _wrapper_view(request, *args, **kwargs): mark_request_as_headless(request, Client.BROWSER) # Needed -- so that the CSRF token is set in the response for the # frontend to pick up. get_token(request) return view_func(request, *args, **kwargs) return _wrapper_view ret = decorator if function: ret = decorator(function) return ret ================================================ FILE: allauth/headless/internal/restkit/__init__.py ================================================ ================================================ FILE: allauth/headless/internal/restkit/inputs.py ================================================ from django.forms import ( BooleanField, CharField, ChoiceField, Field, Form, ModelChoiceField, ModelMultipleChoiceField, ) from allauth.account.fields import EmailField __all__ = [ "Field", "CharField", "ChoiceField", "EmailField", "BooleanField", "ModelMultipleChoiceField", "ModelChoiceField", ] class Input(Form): pass ================================================ FILE: allauth/headless/internal/restkit/response.py ================================================ from http import HTTPStatus from typing import Any from django.forms.utils import ErrorList from django.http import JsonResponse from django.utils.cache import add_never_cache_headers from allauth.headless.internal import authkit, sessionkit class APIResponse(JsonResponse): def __init__( self, request, errors=None, data=None, meta: dict | None = None, status: int = HTTPStatus.OK, ): d: dict[str, Any] = {"status": status} if data is not None: d["data"] = data meta = self._add_session_meta(request, meta) if meta is not None: d["meta"] = meta if errors: d["errors"] = errors super().__init__(d, status=status) add_never_cache_headers(self) def _add_session_meta(self, request, meta: dict | None) -> dict | None: session_token = sessionkit.expose_session_token(request) access_token_payload = authkit.expose_access_token(request) if session_token: meta = meta or {} meta["session_token"] = session_token if access_token_payload: meta = meta or {} meta.update(access_token_payload) return meta class ErrorResponse(APIResponse): def __init__( self, request, exception=None, input=None, status=HTTPStatus.BAD_REQUEST ): errors = [] if exception is not None: error_datas = ErrorList(exception.error_list).get_json_data() errors.extend(error_datas) if input is not None: for field, error_list in input.errors.items(): error_datas = error_list.get_json_data() for error_data in error_datas: if field != "__all__": error_data["param"] = field errors.extend(error_datas) super().__init__(request, status=status, errors=errors) ================================================ FILE: allauth/headless/internal/restkit/views.py ================================================ import json from django.http import HttpResponseBadRequest from django.views.generic import View from allauth.core.exceptions import ImmediateHttpResponse from allauth.headless.internal.restkit.inputs import Input from allauth.headless.internal.restkit.response import ErrorResponse class RESTView(View): input_class: dict[str, type[Input]] | None | type[Input] = None handle_json_input = True def dispatch(self, request, *args, **kwargs): return self.handle(request, *args, **kwargs) def handle(self, request, *args, **kwargs): if self.handle_json_input and request.method != "GET": self.data = self._parse_json(request) response = self.handle_input(self.data) if response: return response return super().dispatch(request, *args, **kwargs) def get_input_class(self): input_class = self.input_class if isinstance(input_class, dict): input_class = input_class.get(self.request.method) return input_class def get_input_kwargs(self) -> dict: return {} def handle_input(self, data): input_class = self.get_input_class() if not input_class: return input_kwargs = self.get_input_kwargs() if data is None: # Make form bound on empty POST data = {} self.input = input_class(data=data, **input_kwargs) if not self.input.is_valid(): return self.handle_invalid_input(self.input) def handle_invalid_input(self, input): return ErrorResponse(self.request, input=input) def _parse_json(self, request): if request.method == "GET" or not request.body: return try: return json.loads(request.body.decode("utf8")) except (UnicodeDecodeError, json.JSONDecodeError): raise ImmediateHttpResponse(response=HttpResponseBadRequest()) ================================================ FILE: allauth/headless/internal/sessionkit.py ================================================ from importlib import import_module from django.conf import settings from django.contrib.auth import SESSION_KEY, get_user_model from django.contrib.sessions.backends.base import SessionBase from allauth.headless import app_settings from allauth.headless.constants import Client def session_store(session_key=None): engine = import_module(settings.SESSION_ENGINE) return engine.SessionStore(session_key=session_key) def new_session() -> SessionBase: return session_store() def expose_session_token(request): if request.allauth.headless.client != Client.APP: return strategy = app_settings.TOKEN_STRATEGY hdr_token = strategy.get_session_token(request) modified = request.session.modified empty = request.session.is_empty() if modified and not empty: new_token = strategy.create_session_token(request) if not hdr_token or hdr_token != new_token: return new_token def authenticate_by_x_session_token(token: str) -> tuple | None: session = app_settings.TOKEN_STRATEGY.lookup_session(token) if not session: return None user_id_str = session.get(SESSION_KEY) if user_id_str: meta_pk = get_user_model()._meta.pk if meta_pk: user_id = meta_pk.to_python(user_id_str) user = get_user_model().objects.filter(pk=user_id).first() if user and user.is_active: return (user, session) return None def lookup_session(session_key: str) -> SessionBase | None: if session_store().exists(session_key): return session_store(session_key) return None ================================================ FILE: allauth/headless/mfa/__init__.py ================================================ ================================================ FILE: allauth/headless/mfa/inputs.py ================================================ from allauth.account.forms import BaseSignupForm from allauth.headless.internal.restkit import inputs from allauth.mfa.base.forms import AuthenticateForm from allauth.mfa.models import Authenticator from allauth.mfa.recovery_codes.forms import GenerateRecoveryCodesForm from allauth.mfa.totp.forms import ActivateTOTPForm from allauth.mfa.webauthn.forms import ( AddWebAuthnForm, AuthenticateWebAuthnForm, LoginWebAuthnForm, ReauthenticateWebAuthnForm, SignupWebAuthnForm, ) class AuthenticateInput(AuthenticateForm, inputs.Input): pass class ActivateTOTPInput(ActivateTOTPForm, inputs.Input): pass class GenerateRecoveryCodesInput(GenerateRecoveryCodesForm, inputs.Input): pass class AddWebAuthnInput(AddWebAuthnForm, inputs.Input): pass class CreateWebAuthnInput(SignupWebAuthnForm, inputs.Input): pass class UpdateWebAuthnInput(inputs.Input): id = inputs.ModelChoiceField(queryset=Authenticator.objects.none()) name = inputs.CharField(required=True, max_length=100) def __init__(self, *args, **kwargs): self.user = kwargs.pop("user") super().__init__(*args, **kwargs) self.fields["id"].queryset = Authenticator.objects.filter( user=self.user, type=Authenticator.Type.WEBAUTHN ) class DeleteWebAuthnInput(inputs.Input): authenticators = inputs.ModelMultipleChoiceField( queryset=Authenticator.objects.none() ) def __init__(self, *args, **kwargs): self.user = kwargs.pop("user") super().__init__(*args, **kwargs) self.fields["authenticators"].queryset = Authenticator.objects.filter( user=self.user, type=Authenticator.Type.WEBAUTHN ) class ReauthenticateWebAuthnInput(ReauthenticateWebAuthnForm, inputs.Input): pass class AuthenticateWebAuthnInput(AuthenticateWebAuthnForm, inputs.Input): pass class LoginWebAuthnInput(LoginWebAuthnForm, inputs.Input): pass class SignupWebAuthnInput(BaseSignupForm, inputs.Input): pass class TrustInput(inputs.Input): trust = inputs.BooleanField(required=False) ================================================ FILE: allauth/headless/mfa/response.py ================================================ from http import HTTPStatus from allauth.headless.base.response import APIResponse from allauth.mfa import app_settings as mfa_settings def get_config_data(request) -> dict: data = { "mfa": { "supported_types": mfa_settings.SUPPORTED_TYPES, "passkey_login_enabled": mfa_settings.PASSKEY_LOGIN_ENABLED, } } return data def _authenticator_data(authenticator, sensitive: bool = False) -> dict: data = { "type": authenticator.type, "created_at": authenticator.created_at.timestamp(), "last_used_at": ( authenticator.last_used_at.timestamp() if authenticator.last_used_at else None ), } if authenticator.type == authenticator.Type.TOTP: pass elif authenticator.type == authenticator.Type.RECOVERY_CODES: wrapped = authenticator.wrap() unused_codes = wrapped.get_unused_codes() data.update( { "total_code_count": len(wrapped.generate_codes()), "unused_code_count": len(unused_codes), } ) if sensitive: data["unused_codes"] = unused_codes elif authenticator.type == authenticator.Type.WEBAUTHN: wrapped = authenticator.wrap() data["id"] = authenticator.pk data["name"] = wrapped.name passwordless = wrapped.is_passwordless if passwordless is not None: data["is_passwordless"] = passwordless return data class AuthenticatorDeletedResponse(APIResponse): pass class AuthenticatorsDeletedResponse(APIResponse): pass class TOTPNotFoundResponse(APIResponse): def __init__(self, request, secret, totp_url): super().__init__( request, meta={ "secret": secret, "totp_url": totp_url, }, status=HTTPStatus.NOT_FOUND, ) class TOTPResponse(APIResponse): def __init__(self, request, authenticator): data = _authenticator_data(authenticator) super().__init__(request, data=data) class AuthenticatorsResponse(APIResponse): def __init__(self, request, authenticators): data = [_authenticator_data(authenticator) for authenticator in authenticators] super().__init__(request, data=data) class AuthenticatorResponse(APIResponse): def __init__(self, request, authenticator, meta=None): data = _authenticator_data(authenticator) super().__init__(request, data=data, meta=meta) class RecoveryCodesNotFoundResponse(APIResponse): def __init__(self, request): super().__init__(request, status=HTTPStatus.NOT_FOUND) class RecoveryCodesResponse(APIResponse): def __init__(self, request, authenticator): data = _authenticator_data(authenticator, sensitive=True) super().__init__(request, data=data) class AddWebAuthnResponse(APIResponse): def __init__(self, request, registration_data): super().__init__(request, data={"creation_options": registration_data}) class WebAuthnRequestOptionsResponse(APIResponse): def __init__(self, request, request_options): super().__init__(request, data={"request_options": request_options}) ================================================ FILE: allauth/headless/mfa/urls.py ================================================ from django.urls import include, path from allauth.headless.constants import Client from allauth.headless.mfa import views from allauth.mfa import app_settings as mfa_settings def build_urlpatterns(client): auth_patterns = [ path( "2fa/authenticate", views.AuthenticateView.as_api_view(client=client), name="authenticate", ), path( "2fa/reauthenticate", views.ReauthenticateView.as_api_view(client=client), name="reauthenticate", ), ] if mfa_settings.TRUST_ENABLED and client == Client.BROWSER: auth_patterns.append( path( "2fa/trust", views.TrustView.as_api_view(client=client), name="trust", ) ) authenticators = [] if "totp" in mfa_settings.SUPPORTED_TYPES: authenticators.append( path( "totp", views.ManageTOTPView.as_api_view(client=client), name="manage_totp", ) ) if "recovery_codes" in mfa_settings.SUPPORTED_TYPES: authenticators.append( path( "recovery-codes", views.ManageRecoveryCodesView.as_api_view(client=client), name="manage_recovery_codes", ) ) if "webauthn" in mfa_settings.SUPPORTED_TYPES: authenticators.extend( [ path( "webauthn", views.ManageWebAuthnView.as_api_view(client=client), name="manage_webauthn", ), ] ) auth_patterns.extend( [ path( "webauthn/authenticate", views.AuthenticateWebAuthnView.as_api_view(client=client), name="authenticate_webauthn", ), path( "webauthn/reauthenticate", views.ReauthenticateWebAuthnView.as_api_view(client=client), name="reauthenticate_webauthn", ), ] ) if mfa_settings.PASSKEY_LOGIN_ENABLED: auth_patterns.append( path( "webauthn/login", views.LoginWebAuthnView.as_api_view(client=client), name="login_webauthn", ) ) if mfa_settings.PASSKEY_SIGNUP_ENABLED: auth_patterns.append( path( "webauthn/signup", views.SignupWebAuthnView.as_api_view(client=client), name="signup_webauthn", ) ) return [ path( "auth/", include(auth_patterns), ), path( "account/", include( [ path( "authenticators", views.AuthenticatorsView.as_api_view(client=client), name="authenticators", ), path( "authenticators/", include(authenticators), ), ] ), ), ] ================================================ FILE: allauth/headless/mfa/views.py ================================================ from http import HTTPStatus from django.core.exceptions import ValidationError from django.http import HttpResponse from allauth.account.internal.stagekit import get_pending_stage from allauth.account.models import Login from allauth.headless.account.views import SignupView from allauth.headless.base.response import ( APIResponse, AuthenticationResponse, ConflictResponse, ) from allauth.headless.base.views import ( APIView, AuthenticatedAPIView, AuthenticationStageAPIView, ) from allauth.headless.internal.restkit.response import ErrorResponse from allauth.headless.mfa import response from allauth.headless.mfa.inputs import ( ActivateTOTPInput, AddWebAuthnInput, AuthenticateInput, AuthenticateWebAuthnInput, CreateWebAuthnInput, DeleteWebAuthnInput, GenerateRecoveryCodesInput, LoginWebAuthnInput, ReauthenticateWebAuthnInput, SignupWebAuthnInput, TrustInput, UpdateWebAuthnInput, ) from allauth.mfa.adapter import DefaultMFAAdapter, get_adapter from allauth.mfa.internal.flows import add from allauth.mfa.internal.flows.trust import trust_browser from allauth.mfa.models import Authenticator from allauth.mfa.recovery_codes.internal import flows as recovery_codes_flows from allauth.mfa.stages import AuthenticateStage, TrustStage from allauth.mfa.totp.internal import auth as totp_auth, flows as totp_flows from allauth.mfa.webauthn.internal import auth as webauthn_auth, flows as webauthn_flows from allauth.mfa.webauthn.stages import PasskeySignupStage def _validate_can_add_authenticator(request): try: add.validate_can_add_authenticator(request.user) except ValidationError as e: return ErrorResponse(request, status=HTTPStatus.CONFLICT, exception=e) class AuthenticateView(AuthenticationStageAPIView): input_class = AuthenticateInput stage_class = AuthenticateStage def post(self, request, *args, **kwargs): self.input.save() return self.respond_next_stage() def get_input_kwargs(self) -> dict: return {"user": self.stage.login.user} class ReauthenticateView(AuthenticatedAPIView): input_class = AuthenticateInput def post(self, request, *args, **kwargs): self.input.save() return AuthenticationResponse(self.request) def get_input_kwargs(self) -> dict: return {"user": self.request.user} class AuthenticatorsView(AuthenticatedAPIView): def get(self, request, *args, **kwargs) -> HttpResponse: authenticators = Authenticator.objects.filter(user=request.user) return response.AuthenticatorsResponse(request, authenticators) class ManageTOTPView(AuthenticatedAPIView): input_class = {"POST": ActivateTOTPInput} def get(self, request, *args, **kwargs) -> APIResponse: authenticator = self._get_authenticator() if not authenticator: err = _validate_can_add_authenticator(request) if err: return err adapter: DefaultMFAAdapter = get_adapter() secret = totp_auth.get_totp_secret(regenerate=True) totp_url: str = adapter.build_totp_url(request.user, secret) return response.TOTPNotFoundResponse(request, secret, totp_url) return response.TOTPResponse(request, authenticator) def _get_authenticator(self): return Authenticator.objects.filter( type=Authenticator.Type.TOTP, user=self.request.user ).first() def get_input_kwargs(self) -> dict: return {"user": self.request.user} def post(self, request, *args, **kwargs): authenticator = totp_flows.activate_totp(request, self.input)[0] return response.TOTPResponse(request, authenticator) def delete(self, request, *args, **kwargs): authenticator = self._get_authenticator() if authenticator: authenticator = totp_flows.deactivate_totp(request, authenticator) return response.AuthenticatorDeletedResponse(request) class ManageRecoveryCodesView(AuthenticatedAPIView): input_class = GenerateRecoveryCodesInput def get(self, request, *args, **kwargs) -> HttpResponse: authenticator = recovery_codes_flows.view_recovery_codes(request) if not authenticator: return response.RecoveryCodesNotFoundResponse(request) return response.RecoveryCodesResponse(request, authenticator) def post(self, request, *args, **kwargs): authenticator = recovery_codes_flows.generate_recovery_codes(request) return response.RecoveryCodesResponse(request, authenticator) def get_input_kwargs(self) -> dict: return {"user": self.request.user} class ManageWebAuthnView(AuthenticatedAPIView): input_class = { "POST": AddWebAuthnInput, "PUT": UpdateWebAuthnInput, "DELETE": DeleteWebAuthnInput, } def handle(self, request, *args, **kwargs): if request.method in ["GET", "POST"]: err = _validate_can_add_authenticator(request) if err: return err return super().handle(request, *args, **kwargs) def get(self, request, *args, **kwargs) -> HttpResponse: passwordless = "passwordless" in request.GET creation_options = webauthn_flows.begin_registration( request, request.user, passwordless ) return response.AddWebAuthnResponse(request, creation_options) def get_input_kwargs(self) -> dict: return {"user": self.request.user} def post(self, request, *args, **kwargs): auth, rc_auth = webauthn_flows.add_authenticator( request, name=self.input.cleaned_data["name"], credential=self.input.cleaned_data["credential"], ) did_generate_recovery_codes = bool(rc_auth) return response.AuthenticatorResponse( request, auth, meta={"recovery_codes_generated": did_generate_recovery_codes}, ) def put(self, request, *args, **kwargs): authenticator = self.input.cleaned_data["id"] webauthn_flows.rename_authenticator( request, authenticator, self.input.cleaned_data["name"] ) return response.AuthenticatorResponse(request, authenticator) def delete(self, request, *args, **kwargs): authenticators = self.input.cleaned_data["authenticators"] webauthn_flows.remove_authenticators(request, authenticators) return response.AuthenticatorsDeletedResponse(request) class ReauthenticateWebAuthnView(AuthenticatedAPIView): input_class = { "POST": ReauthenticateWebAuthnInput, } def get(self, request, *args, **kwargs) -> HttpResponse: request_options = webauthn_auth.begin_authentication(request.user) return response.WebAuthnRequestOptionsResponse(request, request_options) def get_input_kwargs(self) -> dict: return {"user": self.request.user} def post(self, request, *args, **kwargs): authenticator = self.input.cleaned_data["credential"] webauthn_flows.reauthenticate(request, authenticator) return AuthenticationResponse(self.request) class AuthenticateWebAuthnView(AuthenticationStageAPIView): input_class = { "POST": AuthenticateWebAuthnInput, } stage_class = AuthenticateStage def get(self, request, *args, **kwargs) -> HttpResponse: request_options = webauthn_auth.begin_authentication(self.stage.login.user) return response.WebAuthnRequestOptionsResponse(request, request_options) def get_input_kwargs(self) -> dict: return {"user": self.stage.login.user} def post(self, request, *args, **kwargs): self.input.save() return self.respond_next_stage() class LoginWebAuthnView(APIView): input_class = { "POST": LoginWebAuthnInput, } def get(self, request, *args, **kwargs) -> HttpResponse: request_options = webauthn_auth.begin_authentication() return response.WebAuthnRequestOptionsResponse(request, request_options) def post(self, request, *args, **kwargs): authenticator = self.input.cleaned_data["credential"] redirect_url = None login = Login(user=authenticator.user, redirect_url=redirect_url) webauthn_flows.perform_passwordless_login(request, authenticator, login) return AuthenticationResponse(request) class SignupWebAuthnView(SignupView): input_class = { "POST": SignupWebAuthnInput, "PUT": CreateWebAuthnInput, } by_passkey = True def get(self, request, *args, **kwargs) -> HttpResponse: resp = self._require_stage() if resp: return resp creation_options = webauthn_flows.begin_registration( request, self.stage.login.user, passwordless=True, signup=True ) return response.AddWebAuthnResponse(request, creation_options) def _prep_stage(self): if hasattr(self, "stage"): return self.stage self.stage = get_pending_stage(self.request) return self.stage def _require_stage(self): self._prep_stage() if not self.stage or self.stage.key != PasskeySignupStage.key: return ConflictResponse(self.request) return None def get_input_kwargs(self) -> dict: ret = super().get_input_kwargs() self._prep_stage() if self.stage and self.request.method == "PUT": ret["user"] = self.stage.login.user return ret def put(self, request, *args, **kwargs): resp = self._require_stage() if resp: return resp webauthn_flows.signup_authenticator( request, user=self.stage.login.user, name=self.input.cleaned_data["name"], credential=self.input.cleaned_data["credential"], ) self.stage.exit() return AuthenticationResponse(request) class TrustView(AuthenticationStageAPIView): input_class = TrustInput stage_class = TrustStage def post(self, request, *args, **kwargs): trust = self.input.cleaned_data["trust"] response = self.respond_next_stage() if trust: trust_browser(request, self.stage.login.user, response) return response ================================================ FILE: allauth/headless/socialaccount/__init__.py ================================================ ================================================ FILE: allauth/headless/socialaccount/forms.py ================================================ from django import forms from django.core.exceptions import ObjectDoesNotExist from allauth.account.adapter import get_adapter as get_account_adapter from allauth.core import context from allauth.headless.adapter import get_adapter from allauth.socialaccount.adapter import get_adapter as get_socialaccount_adapter from allauth.socialaccount.providers.base.constants import AuthProcess class RedirectToProviderForm(forms.Form): provider = forms.CharField() callback_url = forms.CharField() process = forms.ChoiceField( choices=[ (AuthProcess.LOGIN, AuthProcess.LOGIN), (AuthProcess.CONNECT, AuthProcess.CONNECT), ] ) def clean_callback_url(self) -> str: url = self.cleaned_data["callback_url"] if not get_account_adapter().is_safe_url(url): raise get_adapter().validation_error("invalid_url") return url def clean_provider(self): provider_id = self.cleaned_data["provider"] try: provider = get_socialaccount_adapter().get_provider( context.request, provider_id ) except ObjectDoesNotExist: raise get_adapter().validation_error("unknown_provider") return provider ================================================ FILE: allauth/headless/socialaccount/inputs.py ================================================ from django.core.exceptions import ValidationError from allauth.core import context from allauth.headless.adapter import get_adapter from allauth.headless.internal.restkit import inputs from allauth.socialaccount.adapter import get_adapter as get_socialaccount_adapter from allauth.socialaccount.forms import SignupForm from allauth.socialaccount.internal.flows.connect import validate_disconnect from allauth.socialaccount.models import SocialAccount, SocialApp from allauth.socialaccount.providers import registry from allauth.socialaccount.providers.base.constants import AuthProcess class SignupInput(SignupForm, inputs.Input): pass class DeleteProviderAccountInput(inputs.Input): provider = inputs.CharField() account = inputs.CharField() def __init__(self, *args, **kwargs): self.user = kwargs.pop("user") super().__init__(*args, **kwargs) def clean(self): cleaned_data = super().clean() uid = cleaned_data.get("account") provider_id = cleaned_data.get("provider") if uid and provider_id: accounts = SocialAccount.objects.filter(user=self.user) account = accounts.filter( uid=uid, provider=provider_id, ).first() if not account: raise get_adapter().validation_error("account_not_found") validate_disconnect(context.request, account) self.cleaned_data["account"] = account return cleaned_data class ProviderTokenInput(inputs.Input): provider = inputs.CharField() process = inputs.ChoiceField( choices=[ (AuthProcess.LOGIN, AuthProcess.LOGIN), (AuthProcess.CONNECT, AuthProcess.CONNECT), ] ) token = inputs.Field() def clean(self): cleaned_data = super().clean() token = self.data.get("token") adapter = get_adapter() if not isinstance(token, dict): self.add_error("token", adapter.validation_error("invalid_token")) token = None provider_id = cleaned_data.get("provider") provider = None if provider_id and token: provider_class = registry.get_class(provider_id) # If `provider_id` is a sub provider ID we won't find it by class. client_id_required = provider_class is None or provider_class.uses_apps client_id = token.get("client_id") if client_id_required and not isinstance(client_id, str): self.add_error("token", adapter.validation_error("client_id_required")) else: try: provider = get_socialaccount_adapter().get_provider( context.request, provider_id, client_id=client_id ) except SocialApp.DoesNotExist: self.add_error("token", adapter.validation_error("invalid_token")) else: if not provider.supports_token_authentication: self.add_error( "provider", adapter.validation_error( "token_authentication_not_supported" ), ) elif ( provider.uses_apps and client_id and provider.app.client_id != client_id ): self.add_error( "token", adapter.validation_error("client_id_mismatch") ) else: id_token = token.get("id_token") access_token = token.get("access_token") if ( (id_token is not None and not isinstance(id_token, str)) or ( access_token is not None and not isinstance(access_token, str) ) or (not id_token and not access_token) ): self.add_error( "token", adapter.validation_error("token_required") ) if not self.errors: cleaned_data["provider"] = provider try: login = provider.verify_token(context.request, token) login.state["process"] = cleaned_data["process"] cleaned_data["sociallogin"] = login except ValidationError as e: self.add_error("token", e) return cleaned_data ================================================ FILE: allauth/headless/socialaccount/internal.py ================================================ from django.core.exceptions import PermissionDenied, ValidationError from django.http import HttpResponseRedirect from allauth import app_settings as allauth_settings from allauth.core.exceptions import ( ImmediateHttpResponse, ReauthenticationRequired, SignupClosedException, ) from allauth.core.internal import httpkit from allauth.headless.internal.authkit import AuthenticationStatus from allauth.socialaccount.internal import flows, statekit from allauth.socialaccount.providers.base.constants import AuthError, AuthProcess def on_authentication_error( request, provider, error=None, exception=None, extra_context=None, state_id=None, ) -> None: """ Called at a time when it is not clear whether or not this is a headless flow. """ state = None if extra_context: state = extra_context.get("state") if state is None: state_id = extra_context.get("state_id") if state_id: state = statekit.unstash_state(request, state_id) params = {"error": error} if state is not None: headless = state.get("headless") next_url = state.get("next") params["error_process"] = state["process"] else: headless = allauth_settings.HEADLESS_ONLY next_url = None params["error_process"] = AuthProcess.LOGIN if not headless: return if not next_url: next_url = httpkit.get_frontend_url(request, "socialaccount_login_error") or "/" next_url = httpkit.add_query_params(next_url, params) raise ImmediateHttpResponse(HttpResponseRedirect(next_url)) def complete_token_login(request, sociallogin): return flows.login.complete_login(request, sociallogin, raises=True) def complete_login(request, sociallogin): """ Called when `sociallogin.is_headless`. """ error = None try: flows.login.complete_login(request, sociallogin, raises=True) except ReauthenticationRequired: error = "reauthentication_required" except SignupClosedException: error = "signup_closed" except PermissionDenied: error = "permission_denied" except ValidationError as e: error = e.code else: # At this stage, we're either: # 1) logged in (or in of the login pipeline stages, such as email verification) # 2) auto signed up -- a pipeline stage, so see 1) # 3) performing a social signup # 4) Stopped, due to not being open-for-signup # It would be good to refactor the above into a more generic social login # pipeline with clear stages, but for now the /auth endpoint properly responds status = AuthenticationStatus(request) if all( [ not status.is_authenticated, not status.has_pending_signup, not status.get_pending_stage(), ] ): error = AuthError.UNKNOWN next_url = sociallogin.state["next"] if error: next_url = httpkit.add_query_params( next_url, {"error": error, "error_process": sociallogin.state["process"]}, ) return HttpResponseRedirect(next_url) ================================================ FILE: allauth/headless/socialaccount/response.py ================================================ from allauth.headless.account.response import email_address_data from allauth.headless.adapter import get_adapter from allauth.headless.base.response import APIResponse from allauth.headless.constants import Client, Flow from allauth.socialaccount.adapter import get_adapter as get_socialaccount_adapter from allauth.socialaccount.internal.flows import signup from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider def _socialaccount_data(request, account): return { "uid": account.uid, "provider": _provider_data(request, account.get_provider()), "display": account.get_provider_account().to_str(), } def _provider_data(request, provider): ret = {"id": provider.sub_id, "name": provider.name, "flows": []} if provider.supports_redirect: ret["flows"].append(Flow.PROVIDER_REDIRECT) if provider.supports_token_authentication: ret["flows"].append(Flow.PROVIDER_TOKEN) if isinstance(provider, OAuth2Provider): ret["client_id"] = provider.app.client_id if provider.id == "openid_connect": ret["openid_configuration_url"] = provider.server_url return ret def provider_flows(request): flows = [] providers = _list_supported_providers(request) if providers: redirect_providers = [p.sub_id for p in providers if p.supports_redirect] token_providers = [ p.sub_id for p in providers if p.supports_token_authentication ] if redirect_providers and request.allauth.headless.client == Client.BROWSER: flows.append( { "id": Flow.PROVIDER_REDIRECT, "providers": redirect_providers, } ) if token_providers: flows.append( { "id": Flow.PROVIDER_TOKEN, "providers": token_providers, } ) sociallogin = signup.get_pending_signup(request) if sociallogin: flows.append(_signup_flow(request, sociallogin)) return flows def _signup_flow(request, sociallogin): provider = sociallogin.provider flow = { "id": Flow.PROVIDER_SIGNUP, "provider": _provider_data(request, provider), "is_pending": True, } return flow def _is_provider_supported(provider, client): if client == Client.APP: return provider.supports_token_authentication elif client == Client.BROWSER: return provider.supports_redirect return False def _list_supported_providers(request): adapter = get_socialaccount_adapter() providers = adapter.list_providers(request) providers = [ p for p in providers if _is_provider_supported(p, request.allauth.headless.client) ] return providers def get_config_data(request): entries = [] data = {"socialaccount": {"providers": entries}} providers = _list_supported_providers(request) providers = sorted(providers, key=lambda p: p.name) for provider in providers: entries.append(_provider_data(request, provider)) return data class SocialAccountsResponse(APIResponse): def __init__(self, request, accounts): data = [_socialaccount_data(request, account) for account in accounts] super().__init__(request, data=data) class SocialLoginResponse(APIResponse): def __init__(self, request, sociallogin): adapter = get_adapter() data = { "user": adapter.serialize_user(sociallogin.user), "account": _socialaccount_data(request, sociallogin.account), "email": [email_address_data(ea) for ea in sociallogin.email_addresses], } super().__init__(request, data=data) ================================================ FILE: allauth/headless/socialaccount/urls.py ================================================ from django.urls import include, path from allauth.headless.socialaccount import views def build_urlpatterns(client): return [ path( "account/", include( [ path( "providers", views.ManageProvidersView.as_api_view(client=client), name="manage_providers", ), ] ), ), path( "auth/", include( [ path( "provider/", include( [ path( "signup", views.ProviderSignupView.as_api_view(client=client), name="provider_signup", ), path( "redirect", views.RedirectToProviderView.as_api_view( client=client ), name="redirect_to_provider", ), path( "token", views.ProviderTokenView.as_api_view(client=client), name="provider_token", ), ] ), ) ] ), ), ] ================================================ FILE: allauth/headless/socialaccount/views.py ================================================ from django.core.exceptions import ValidationError from django.http import HttpResponse from allauth.core.exceptions import SignupClosedException from allauth.headless.base.response import ( AuthenticationResponse, ConflictResponse, ForbiddenResponse, ) from allauth.headless.base.views import APIView, AuthenticatedAPIView from allauth.headless.internal.restkit.response import ErrorResponse from allauth.headless.socialaccount.forms import RedirectToProviderForm from allauth.headless.socialaccount.inputs import ( DeleteProviderAccountInput, ProviderTokenInput, SignupInput, ) from allauth.headless.socialaccount.internal import complete_token_login from allauth.headless.socialaccount.response import ( SocialAccountsResponse, SocialLoginResponse, ) from allauth.socialaccount.adapter import get_adapter as get_socialaccount_adapter from allauth.socialaccount.helpers import render_authentication_error from allauth.socialaccount.internal import flows from allauth.socialaccount.models import SocialAccount class ProviderSignupView(APIView): input_class = SignupInput def handle(self, request, *args, **kwargs): self.sociallogin = flows.signup.get_pending_signup(self.request) if not self.sociallogin: return ConflictResponse(request) if not get_socialaccount_adapter().is_open_for_signup( request, self.sociallogin ): return ForbiddenResponse(request) return super().handle(request, *args, **kwargs) def get(self, request, *args, **kwargs) -> HttpResponse: return SocialLoginResponse(request, self.sociallogin) def post(self, request, *args, **kwargs): response = flows.signup.signup_by_form( self.request, self.sociallogin, self.input ) return AuthenticationResponse.from_response(request, response) def get_input_kwargs(self) -> dict: return {"sociallogin": self.sociallogin} class RedirectToProviderView(APIView): handle_json_input = False def post(self, request, *args, **kwargs): form = RedirectToProviderForm(request.POST) if not form.is_valid(): return render_authentication_error( request, provider=request.POST.get("provider"), exception=ValidationError(form.errors), ) provider = form.cleaned_data["provider"] next_url = form.cleaned_data["callback_url"] process = form.cleaned_data["process"] return provider.redirect( request, process, next_url=next_url, headless=True, ) class ManageProvidersView(AuthenticatedAPIView): input_class = { "DELETE": DeleteProviderAccountInput, } def get(self, request, *args, **kwargs) -> HttpResponse: return self.respond_provider_accounts(request) @classmethod def respond_provider_accounts(self, request): accounts = SocialAccount.objects.filter(user=request.user) return SocialAccountsResponse(request, accounts) def delete(self, request, *args, **kwargs): flows.connect.disconnect(request, self.input.cleaned_data["account"]) return self.respond_provider_accounts(request) def get_input_kwargs(self) -> dict: return {"user": self.request.user} class ProviderTokenView(APIView): input_class = ProviderTokenInput def post(self, request, *args, **kwargs): sociallogin = self.input.cleaned_data["sociallogin"] response = None try: response = complete_token_login(request, sociallogin) except ValidationError as e: return ErrorResponse(self.request, exception=e) except SignupClosedException: return ForbiddenResponse(self.request) return AuthenticationResponse.from_response(self.request, response) ================================================ FILE: allauth/headless/spec/__init__.py ================================================ ================================================ FILE: allauth/headless/spec/doc/description.md ================================================ # Introduction Welcome to the django-allauth API specification. This API is intended to be consumed by two different kind of clients: - Web applications running in a **browser** context. For example, a single-page React application, to which the end user can navigate using a web browser. - Applications, **apps** for short, executing in non-browser contexts. For example, a mobile Android or iOS application. The security considerations for these two usage types are different. In a browser context, cookies play a role. Without taking special precautions, your web application may be vulnerable to Cross-Site Request Forgery attacks. For mobile applications, this does not apply. The API can be used for both use cases. Differences in handling of security is automatically adjusted for, based on the request path used to make the API call. For example, signing up can either be done using the `/_allauth/browser/v1/auth/signup` or the `/_allauth/app/v1/auth/signup` endpoint. For the **browser** usage, session cookies and CSRF protection applies. For the **app** usage, cookies play no role, instead, a session token is used. The paths of all endpoints are documented in the form of `/_allauth/{client}/v1/auth/signup`. Depending on the client type (`{client}`), there may be slight differences in request/response handling. This is documented where applicable. # Scope The following functionality is all in scope and handled as part of this API: - Regular accounts: - Login - Signup - Password forgotten - Manage email (add, remove, verify, select a different primary) - Change password. - Verification of email addresses. - Two-Factor Authentication: - Authentication using an authenticator code - (De)activate TOTP - (Re)generate recovery codes - "Trust this browser" - Third-party providers: - Authenticate by performing a browser-level redirect (synchronous request). - Authenticate by means of a provider token. - Connect additional provider accounts. - Disconnect existing provider accounts. - Setting a password in case no password was set, yet. - Querying additional information before signing up. - Session management: - Listing all sessions for a user. - Signing out of any of those sessions. # Browser Usage For web applications running in a browser, routing needs to be setup correctly such that the sessions initiated at the backend are accessible in the frontend. ## Routing When using the API in a browser context, regular Django sessions are used, along with the usual session cookies. There are several options for setting up the routing of your application. ### Single Domain Routing With single domain, path-based routing, both your frontend and backend are served from the same domain, for example `https://app.org`. You will have to make sure that some paths are served by the frontend, and others by the backend. ### Sub-domain Routing With sub-domain based routing, the frontend and backend are served from different domains. However, as session cookies are used, these different domains must share common main domain. For example, you may use `app.project.org` for the frontend, which interfaces with the backend over at `backend.project.org`. In this setup, Django will need to be configured with: ``` SESSION_COOKIE_DOMAIN = "project.org" CSRF_COOKIE_DOMAIN = "project.org" ``` If your organization hosts unrelated applications, for example, a CMS for marketing purposes, on the top level domain (`project.org`), it is not advisable to set the session cookie domain to `project.org`, as those other applications could get access to the session cookie. In that case, it is advised to use `backend.app.project.org` for the backend, and set the session cookie domain to `app.project.org`. # App Usage For app based usage, cookies play no role, yet, sessions are still used. When a user walks through the authentication flow, a session is created. Having an authenticated session is proof that the user is allowed to further interact with the backend. Unauthenticated sessions are also needed to remember state while the user proceeds to go over the required steps necessary to authenticate. ## Session Tokens Given that there is no cookie to point to the session, the header `X-Session-Token` is used instead. The way of working is as follows: - If you do not have a session token yet, do not send the `X-Session-Token` header. - When making requests, session tokens can appear in the metadata (`meta.session_token`) of authentication related responses. If a session token appears, store it (overwriting any previous session token), and ensure to add the token to the `X-Session-Token` header of all subsequent requests. - When receiving an authentication related response with status code 410 (`Gone`), that is meant to indicate that the session is no longer valid. Remove the session token and start clean. ## Access Tokens While session tokens are required to handle the authentication process, depending on your requirements, a different type of token may be needed once authenticated. For example, your app likely needs access to other APIs as well. These APIs may even be implemented using different technologies, in which case having a stateless token, possibly a JWT encoding the user ID, might be a good fit. In this API and its implementation no assumptions, and no (limiting) design decisions are made in this regard. The token strategy of django-allauth is pluggable, such that you can expose your own access token when the user authenticates. As for as the API specification is concerned, the access token will appear in the response of metadata (`meta.access_token`) of a successful authentication request. How you can customize the token strategy can be found over at the documentation of the `allauth.headless` Django application. # Responses Unless documented otherwise, responses are objects with the following properties: - The `status`, matching the HTTP status code. - Data, if any, is returned as part of the `data` key. - Metadata, if any, is returned as part of the `meta` key. - Errors, if any, are listed in the `errors` key. # Authentication Flows In order to become authenticated, the user must complete a flow, potentially consisting of several steps. For example: - A login, after which the user is authenticated. - A Login, followed by two-factor authentication, after which the user is authenticated. - A signup, followed by mandatory email verification, after which the user is authenticated. The API signals to the client that (re)authentication is required by means of a `401` or `410` status code: - Not authenticated: status `401`. - Re-authentication required: status `401`, with `meta.is_authenticated = true`. - Invalid session: status `410`. This only occurs for clients of type `app`. All authentication related responses have status `401` or `410`, and, `meta.is_authenticated` indicating whether authentication, or re-authentication is required. The flows the client can perform to initiate or complete the authentication are communicates as part of authentication related responses. The authentication can be initiated by means of these flows: - Login using a local account (`login`). - Signup for a local account (`signup`). - Login or signup using the third-party provider redirect flow (`provider_redirect`). - Login or signup by handing over a third-party provider retrieved elsewhere (`provider_token`). - Login using a special code (`login_by_code`). - Login using a passkey (`mfa_login_webauthn`). - Signup using a passkey (`mfa_signup_webauthn`). Depending on the state of the account, and the configuration of django-allauth, the flows above can either lead to becoming directly authenticated, or, to followup flows: - Provider signup (`provider_signup`). - Email verification (`verify_email`). - Phone verification (`phone_email`). - Two-factor authentication required (TOTP, recovery codes, or WebAuthn) (`mfa_authenticate`). - Trust this browser (`mfa_trust`). While authenticated, re-authentication may be required to safeguard the account when sensitive actions are performed. The re-authentication flows are the following: - Re-authenticate using password (`reauthenticate`). - Re-authenticate using a 2FA authenticator (TOTP, recovery codes, or WebAuthn) (`mfa_reauthenticate`). # Security Considerations ## Input Sanitization The Django framework, by design, does *not* perform input sanitization. For example, there is nothing preventing end users from signing up using `", "credential": { "type": "public-key", ... } } ================================================ FILE: allauth/headless/spec/doc/openapi.yaml ================================================ openapi: 3.0.3 info: version: "1" title: "django-allauth: Headless API" description: $ref: "./description.md" contact: email: info@allauth.org license: name: MIT url: https://opensource.org/license/mit externalDocs: description: The django-allauth project. url: http://allauth.org tags: - name: Configuration description: | Exposes information on the configuration of django-allauth. - name: "Authentication: Account" description: | All functionality related towards authenticating regular username/email-password based accounts. - name: "Account: Email" description: | The API used for manipulating the email addresses attached to a given account. This is intentionally modeled as one endpoint, representing the collection of all the email addresses. Note that manipulating one email address may affect another. For example, marking one email address as primary implies the previous primary email address is changed as well. Also, if django-allauth is configured with `ACCOUNT_CHANGE_EMAIL = True`, verifying the email address the user is changing to will cause the previous email addres to be removed. - name: "Account: Phone" description: | The API used for manipulating the phone number attached to a given account. - name: "Account: Password" description: | Endpoints that can be used to alter the password for a given account. - name: "Account: Providers" description: | Management of third-party provider accounts that are connected to the authenticated account. - name: "Authentication: 2FA" description: | Endpoints related towards completing the Two-Factor Authentication stage during the authentication cycle. x-tagGroups: - name: Overall tags: - "Configuration" - name: Authentication tags: - "Authentication: Current Session" - "Authentication: Account" - "Authentication: Password Reset" - "Authentication: Providers" - "Authentication: 2FA" - "Authentication: Login By Code" - "Authentication: WebAuthn: Login" - "Authentication: WebAuthn: Signup" - name: Account tags: - "Account: Email" - "Account: Password" - "Account: Phone" - "Account: Providers" - "Account: 2FA" - "Account: WebAuthn" - name: Sessions tags: - "Sessions" - name: Tokens tags: - "Tokens" paths: ###################################################################### # Base ###################################################################### /_allauth/{client}/v1/config: get: summary: Get configuration tags: - Configuration description: | There are many configuration options that alter the functionality and behavior of django-allauth, some of which can also impact the frontend. Therefore, relevant configuration options are exposed via this endpoint. The data returned is not user/authentication dependent. Hence, it suffices to only fetch this data once at boot time of your application. parameters: - $ref: "#/components/parameters/Client" responses: "200": $ref: "#/components/responses/Configuration" ###################################################################### # Authentication: Account ###################################################################### /_allauth/{client}/v1/auth/login: post: tags: - "Authentication: Account" summary: Login description: | Login using a username-password or email-password combination. parameters: - $ref: "#/components/parameters/Client" requestBody: $ref: "#/components/requestBodies/Login" responses: "200": $ref: "#/components/responses/AuthenticatedByPassword" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_email: $ref: "#/components/examples/InvalidEmail" password_mismatch: $ref: "#/components/examples/PasswordMismatch" "401": description: | Not authenticated. content: application/json: schema: $ref: "#/components/schemas/AuthenticationResponse" examples: pending_email: $ref: "#/components/examples/UnauthenticatedPendingEmailVerification" pending_2fa: $ref: "#/components/examples/UnauthenticatedPending2FA" "409": description: | Conflict. For example, when logging in when a user is already logged in. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" /_allauth/{client}/v1/auth/signup: post: tags: - "Authentication: Account" summary: Signup description: | Whether or not `username`, `email`, `phone` or combination of those are required depends on the configuration of django-allauth. Additionally, if a custom signup form is used there may be other custom properties required. parameters: - $ref: "#/components/parameters/Client" requestBody: $ref: "#/components/requestBodies/Signup" responses: "200": $ref: "#/components/responses/AuthenticatedByPassword" "400": description: | An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_email: $ref: "#/components/examples/InvalidEmail" "401": description: | Not authenticated. content: application/json: schema: $ref: "#/components/schemas/AuthenticationResponse" examples: pending_email: $ref: "#/components/examples/UnauthenticatedPendingEmailVerification" "403": description: | Forbidden. For example, when signup is closed. content: application/json: schema: $ref: "#/components/schemas/ForbiddenResponse" "409": description: | Conflict. For example, when signing up while user is logged in. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" /_allauth/{client}/v1/auth/email/verify: get: tags: - "Authentication: Account" summary: Get email verification information description: | Obtain email verification information, given the token that was sent to the user by email. parameters: - $ref: "#/components/parameters/EmailVerificationKey" - $ref: "#/components/parameters/Client" responses: "200": $ref: "#/components/responses/EmailVerificationInfo" "400": description: | An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_email: $ref: "#/components/examples/InvalidEmailVerificationKey" "409": description: | Conflict. The email verification (by code) flow is not pending. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" post: tags: - "Authentication: Account" summary: Verify an email description: | Complete the email verification process. Depending on the configuration, email addresses are either verified by opening a link that is sent to their email address, or, by inputting a code that is sent. On the API, both cases are handled identically. Meaning, the required key is either the one from the link, or, the code itself. Note that a status code of 401 does not imply failure. It indicates that the email verification was successful, yet, the user is still not signed in. For example, in case `ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION` is set to `False`, a 401 is returned when verifying as part of login/signup. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/VerifyEmail" responses: "200": $ref: "#/components/responses/Authenticated" "400": description: | An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_email: $ref: "#/components/examples/InvalidEmailVerificationKey" "401": $ref: "#/components/responses/Unauthenticated" "409": description: | Conflict. The email verification (by code) flow is not pending. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" /_allauth/{client}/v1/auth/email/verify/resend: post: tags: - "Authentication: Account" summary: Resend email verification code description: | Requests a new email verification code. Requires `ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_RESEND = True`. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" responses: "200": $ref: "#/components/responses/StatusOK" "409": description: | Conflict. The email verification (by code) flow is not pending. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" "429": $ref: "#/components/responses/TooManyRequests" /_allauth/{client}/v1/auth/phone/verify: post: tags: - "Authentication: Account" summary: Verify a phone number description: | Complete the phone number verification process. Note that a status code of 401 does not imply failure. It merely indicates that the phone number verification was successful, yet, the user is still not signed in. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/VerifyPhone" responses: "200": $ref: "#/components/responses/Authenticated" "400": description: | An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" "401": $ref: "#/components/responses/Unauthenticated" "409": description: | Conflict. The phone verification flow is not pending. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" /_allauth/{client}/v1/auth/phone/verify/resend: post: tags: - "Authentication: Account" summary: Resend phone number verification code description: | Requests a new phone number verification code. Requires `ACCOUNT_PHONE_VERIFICATION_SUPPORTS_RESEND = True`. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" responses: "200": $ref: "#/components/responses/StatusOK" "409": description: | Conflict. The phone verification flow is not pending. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" "429": $ref: "#/components/responses/TooManyRequests" /_allauth/{client}/v1/auth/reauthenticate: post: tags: - "Authentication: Account" summary: Reauthenticate description: | In order to safeguard the account, some actions require the user to be recently authenticated. If you try to perform such an action without having been recently authenticated, a `401` status is returned, listing flows that can be performed to reauthenticate. One such flow is the flow with ID `reauthenticate`, which allows for the user to input the password. This is the endpoint related towards that flow. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/Reauthenticate" responses: "200": $ref: "#/components/responses/AuthenticatedByPassword" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_email: $ref: "#/components/examples/IncorrectPassword" ###################################################################### # Authentication: Password ###################################################################### /_allauth/{client}/v1/auth/password/request: post: summary: Request password description: | Initiates the password reset procedure. Depending on whether or not `ACCOUNT_PASSWORD_RESET_BY_CODE_ENABLED` is `True`, the procedure is either stateless or stateful. In case codes are used, it is stateful, and a new `password_reset_by_code` flow is started. In this case, on a successful password reset request, you will receive a 401 indicating the pending status of this flow. In case password reset is configured to use (stateless) links, you will receive a 200 on a successful password reset request. tags: - "Authentication: Password Reset" parameters: - $ref: "#/components/parameters/Client" requestBody: $ref: "#/components/requestBodies/RequestPassword" responses: "200": $ref: "#/components/responses/StatusOK" "400": description: | An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_email: $ref: "#/components/examples/InvalidEmail" "401": $ref: "#/components/responses/Authentication" /_allauth/{client}/v1/auth/password/reset: get: summary: Get password reset information description: | Used to obtain information on and validate a password reset key. The key passed is either the key encoded in the password reset URL that the user has received per email, or, the password reset code in case of `ACCOUNT_PASSWORD_RESET_BY_CODE_ENABLED`. Note that in case of a code, the number of requests you can make is limited (by `ACCOUNT_PASSWORD_RESET_BY_CODE_MAX_ATTEMPTS`). tags: - "Authentication: Password Reset" parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/PasswordResetKey" responses: "200": $ref: "#/components/responses/PasswordResetInfo" "400": description: | An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: password_reset_key_invalid: $ref: "#/components/examples/InvalidPasswordResetKey" "409": description: | Conflict. There is no password reset (by code) flow pending. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" post: summary: Reset password description: | Perform the password reset, by handing over the password reset key and the new password. After successfully completing the password reset, the user is either logged in (in case `ACCOUNT_LOGIN_ON_PASSWORD_RESET` is `True`), or, the user will need to proceed to the login page. In case of the former, a `200` status code is returned, in case of the latter a 401. tags: - "Authentication: Password Reset" parameters: - $ref: "#/components/parameters/Client" requestBody: $ref: "#/components/requestBodies/ResetPassword" responses: "200": $ref: "#/components/responses/AuthenticatedByPassword" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_email: $ref: "#/components/examples/InvalidEmail" "401": $ref: "#/components/responses/Authentication" "409": description: | Conflict. There is no password reset (by code) flow pending. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" ###################################################################### # Authentication: Providers ###################################################################### /_allauth/browser/v1/auth/provider/redirect: post: tags: - "Authentication: Providers" summary: Provider redirect description: | Initiates the third-party provider authentication redirect flow. As calling this endpoint results in a user facing redirect (302), this call is only available in a browser, and must be called in a synchronous (non-XHR) manner. requestBody: $ref: "#/components/requestBodies/ProviderRedirect" responses: "302": description: The provider authorization URL to which the client should be redirected. headers: location: schema: type: string description: The redirect URL. /_allauth/{client}/v1/auth/provider/token: post: tags: - "Authentication: Providers" summary: Provider token description: | Authenticates with a third-party provider using provider tokens received by other means. For example, in case of a mobile app, the authentication flow runs completely on the device itself, without any interaction with the API. Then, when the (device) authentication completes and the mobile app receives an access and/or ID token, it can hand over these tokens via this endpoint to authenticate on the server. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/ProviderToken" responses: "200": $ref: "#/components/responses/Authenticated" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_token: $ref: "#/components/examples/InvalidProviderToken" "401": description: Not authenticated, more steps are required to be completed. content: application/json: schema: $ref: "#/components/schemas/AuthenticationResponse" examples: unauthenticated_pending_2fa: $ref: "#/components/examples/UnauthenticatedPending2FA" unauthenticated_pending_email_verification: $ref: "#/components/examples/UnauthenticatedPendingEmailVerification" "403": description: | Forbidden. For example, when signup is closed. content: application/json: schema: $ref: "#/components/schemas/ForbiddenResponse" /_allauth/{client}/v1/auth/provider/signup: get: tags: - "Authentication: Providers" summary: Provider signup information description: | If, while signing up using a third-party provider account, there is insufficient information received from the provider to automatically complete the signup process, an additional step is needed to complete the missing data before the user is fully signed up and authenticated. The information available so far, such as the pending provider account, can be retrieved via this endpoint. parameters: - $ref: "#/components/parameters/Client" responses: "200": $ref: "#/components/responses/ProviderSignup" "409": description: | Conflict. The provider signup flow is not pending. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" post: tags: - "Authentication: Providers" summary: Provider signup description: | If, while signing up using a third-party provider account, there is insufficient information received from the provider to automatically complete the signup process, an additional step is needed to complete the missing data before the user is fully signed up and authenticated. parameters: - $ref: "#/components/parameters/Client" requestBody: $ref: "#/components/requestBodies/ProviderSignup" responses: "200": $ref: "#/components/responses/Authenticated" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_email: $ref: "#/components/examples/InvalidEmail" "401": description: Not authenticated, more steps are required to be completed. content: application/json: schema: $ref: "#/components/schemas/AuthenticationResponse" examples: unauthenticated_pending_email_verification: $ref: "#/components/examples/UnauthenticatedPendingEmailVerification" "403": description: | Forbidden. For example, when signup is closed. content: application/json: schema: $ref: "#/components/schemas/ForbiddenResponse" "409": description: | Conflict. The provider signup flow is not pending. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" ###################################################################### # Authentication: 2FA ###################################################################### /_allauth/{client}/v1/auth/2fa/authenticate: post: tags: - "Authentication: 2FA" summary: Two-factor authentication description: | If, during authentication, a response with status 401 is encountered where one of the pending flows has ID `mfa_authenticate`, that indicates that the Two-Factor Authentication stage needs to be completed. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/MFAAuthenticate" responses: "200": $ref: "#/components/responses/AuthenticatedByPasswordAnd2FA" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_code: $ref: "#/components/examples/InvalidAuthenticatorCode" "401": $ref: "#/components/responses/Authentication" /_allauth/{client}/v1/auth/2fa/reauthenticate: post: tags: - "Authentication: 2FA" summary: Reauthenticate using 2FA description: | In order to safeguard the account, some actions require the user to be recently authenticated. If you try to perform such an action without having been recently authenticated, a `401` status is returned, listing flows that can be performed to reauthenticate. One such flow is the flow with ID `mfa_reauthenticate`, which allows for the user to input an authenticator code (e.g. TOTP or recovery code). This is the endpoint related towards that flow. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: '#/components/requestBodies/MFAAuthenticate' responses: "200": $ref: "#/components/responses/AuthenticatedByPasswordAnd2FA" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_code: $ref: "#/components/examples/InvalidAuthenticatorCode" /_allauth/browser/v1/auth/2fa/trust: post: tags: - "Authentication: 2FA" summary: Trust this browser description: | If "Trust this browser?" is enabled (`MFA_TRUST_ENABLED`), the `mfa_trust` flow activates after the user completes the MFA authentication flow, offering to skip MFA for this particular browser. This endpoint is used to complete the `mfa_trust` flow. parameters: - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/MFATrust" responses: "200": $ref: "#/components/responses/AuthenticatedByPasswordAnd2FA" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" ###################################################################### # Authentication: WebAuthn ###################################################################### /_allauth/{client}/v1/auth/webauthn/authenticate: get: tags: - "Authentication: WebAuthn: Login" summary: Get WebAuthn credential request options for 2FA parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" description: | Returns the WebAuthn credential request options, that can be processed using `parseRequestOptionsFromJSON()` on the frontend. responses: "200": $ref: "#/components/responses/WebAuthnRequestOptionsResponse" post: tags: - "Authentication: WebAuthn: Login" summary: Perform 2FA using WebAuthn parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" description: | Perform Two-Factor Authentication using a WebAuthn credential. requestBody: $ref: "#/components/requestBodies/AuthenticateWebAuthn" responses: "200": $ref: "#/components/responses/Authenticated" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" /_allauth/{client}/v1/auth/webauthn/reauthenticate: get: tags: - "Authentication: WebAuthn: Login" summary: Get WebAuthn credential request options for reauthentication parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" description: | Returns the WebAuthn credential request options, that can be processed using `parseRequestOptionsFromJSON()` on the frontend. responses: "200": $ref: "#/components/responses/WebAuthnRequestOptionsResponse" post: tags: - "Authentication: WebAuthn: Login" summary: Reauthenticate using WebAuthn parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" description: | Reauthenticate the user using a WebAuthn credential. requestBody: $ref: "#/components/requestBodies/ReauthenticateWebAuthn" responses: "200": $ref: "#/components/responses/Authenticated" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" /_allauth/{client}/v1/auth/webauthn/login: get: tags: - "Authentication: WebAuthn: Login" summary: Get WebAuthn credential request options for login parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" description: | Returns the WebAuthn credential request options, that can be processed using `parseRequestOptionsFromJSON()` on the frontend. responses: "200": $ref: "#/components/responses/WebAuthnRequestOptionsResponse" post: tags: - "Authentication: WebAuthn: Login" summary: Login using WebAuthn parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" description: | Login using a WebAuthn credential (Passkey). Both 200 and 401 can be expected after a successful request. The 401 can, for example, occur when the credential passed was valid, but the email attached to the account still requires verification. requestBody: $ref: "#/components/requestBodies/LoginWebAuthn" responses: "200": $ref: "#/components/responses/Authenticated" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" "401": description: | Not authenticated. content: application/json: schema: $ref: "#/components/schemas/AuthenticationResponse" examples: pending_email: $ref: "#/components/examples/UnauthenticatedPendingEmailVerification" /_allauth/{client}/v1/auth/webauthn/signup: post: tags: - "Authentication: WebAuthn: Signup" summary: Initiate the passkey signup flow parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" description: | You initiate the passkey signup flow by inputting (`POST`) the required properties (e.g. email) similar to the regular account signup, except that the `password` is to be left out. The user will then be required to verify the email address, after which WebAuthn credential creation options can be retrieved (`GET`) and used to actually complete (`PUT`) the flow. requestBody: $ref: "#/components/requestBodies/PasskeySignup" responses: "400": description: | An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_email: $ref: "#/components/examples/InvalidEmail" "401": description: | Not authenticated, email verification pending. content: application/json: schema: $ref: "#/components/schemas/AuthenticationResponse" examples: pending_email: $ref: "#/components/examples/UnauthenticatedPendingEmailVerification" "403": description: | Forbidden. For example, when signup is closed. content: application/json: schema: $ref: "#/components/schemas/ForbiddenResponse" "409": description: | Conflict. For example, when signing up while user is logged in. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" get: tags: - "Authentication: WebAuthn: Signup" summary: Get passkey credential request options parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" description: | Returns the WebAuthn credential request options, that can be processed using `parseRequestOptionsFromJSON()` on the frontend. responses: "200": $ref: "#/components/responses/WebAuthnRequestOptionsResponse" "409": description: | Conflict. For example, when the passkey signup flow is not pending. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" put: tags: - "Authentication: WebAuthn: Signup" summary: Complete the passkey signup flow parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" description: | Complete the passkey signup flow by handing over the WebAuthn credential. requestBody: $ref: "#/components/requestBodies/AddWebAuthnAuthenticator" responses: "200": $ref: "#/components/responses/Authenticated" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" "401": description: | Not authenticated. content: application/json: schema: $ref: "#/components/schemas/AuthenticationResponse" examples: pending_email: $ref: "#/components/examples/UnauthenticatedPendingEmailVerification" "409": description: | Conflict. For example, when the passkey signup flow is not pending. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" ###################################################################### # Authentication: Login by Code ###################################################################### /_allauth/{client}/v1/auth/code/request: post: tags: - "Authentication: Login By Code" summary: Request login code description: | Request a "special" login code that is sent to the user by email. parameters: - $ref: "#/components/parameters/Client" requestBody: $ref: "#/components/requestBodies/RequestLoginCode" responses: "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_email: $ref: "#/components/examples/InvalidEmail" "401": description: | Not authenticated. content: application/json: schema: $ref: "#/components/schemas/AuthenticationResponse" examples: pending_login_by_code: $ref: "#/components/examples/UnauthenticatedPendingLoginByCode" /_allauth/{client}/v1/auth/code/confirm: post: tags: - "Authentication: Login By Code" summary: Confirm login code description: | Use this endpoint to pass along the received "special" login code. parameters: - $ref: "#/components/parameters/Client" requestBody: $ref: "#/components/requestBodies/ConfirmLoginCode" responses: "200": $ref: "#/components/responses/AuthenticatedByCode" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_code: $ref: "#/components/examples/InvalidAuthenticatorCode" "401": description: | Not authenticated. content: application/json: schema: $ref: "#/components/schemas/AuthenticationResponse" examples: unauthenticated_pending_2fa: $ref: "#/components/examples/UnauthenticatedPending2FA" "409": description: | Conflict. The "login by code" flow is not pending. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" /_allauth/{client}/v1/auth/code/resend: post: tags: - "Authentication: Login By Code" summary: Resend login code description: | Requests a new login code. Requires `ACCOUNT_LOGIN_BY_CODE_SUPPORTS_RESEND = True`. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" responses: "200": $ref: "#/components/responses/StatusOK" "409": description: | Conflict. The login verification (by code) flow is not pending. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" "429": $ref: "#/components/responses/TooManyRequests" ###################################################################### # Account: Providers ###################################################################### /_allauth/{client}/v1/account/providers: get: tags: - "Account: Providers" summary: List the connected third-party provider accounts parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" responses: "200": $ref: "#/components/responses/ProviderAccounts" delete: tags: - "Account: Providers" summary: | Disconnect a third-party provider account description: | Disconnect a third-party provider account, returning the remaining accounts that are still connected. The disconnect is not allowed if it would leave the account unusable. For example, if no password was set up yet. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/ProviderAccount" responses: "200": $ref: "#/components/responses/ProviderAccounts" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: no_password: $ref: "#/components/examples/DisconnectNotAllowedNoPassword" no_email: $ref: "#/components/examples/DisconnectNotAllowedNoVerifiedEmail" ###################################################################### # Account: Email ###################################################################### /_allauth/{client}/v1/account/email: get: tags: - "Account: Email" summary: List email addresses description: | Retrieves the list of email addresses of the account. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" responses: "200": $ref: "#/components/responses/EmailAddresses" "401": $ref: "#/components/responses/Authentication" post: tags: - "Account: Email" summary: | Add/Change email address description: | The following functionality is available: - Adding a new email address for an already signed in user (`ACCOUNT_CHANGE_EMAIL = False`). - Change to a new email address for an already signed in user (`ACCOUNT_CHANGE_EMAIL = True`). - Change to a new email address during the email verification process at signup (`ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_CHANGE = True`). In all cases, an email verification mail will be sent containing a link or code that needs to be verified. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/Email" responses: "200": $ref: "#/components/responses/EmailAddresses" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_code: $ref: "#/components/examples/InvalidEmail" "401": $ref: "#/components/responses/AuthenticationOrReauthentication" "409": description: | Conflict. For example, when no user is authenticated and no email verification flow is pending. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" put: tags: - "Account: Email" summary: Request email verification description: | Requests for (another) email verification email to be sent. Note that sending emails is rate limited, so when you send too many requests the email will not be sent. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/Email" responses: "200": $ref: "#/components/responses/StatusOK" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_code: $ref: "#/components/examples/InvalidEmail" "403": description: | Too many email verification mails were already sent. content: application/json: schema: $ref: "#/components/schemas/ForbiddenResponse" patch: tags: - "Account: Email" summary: Change primary email address description: | Used to change primary email address to a different one. Note that only verified email addresses can be marked as primary. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/MarkPrimaryEmail" responses: "200": $ref: "#/components/responses/EmailAddresses" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_code: $ref: "#/components/examples/InvalidEmail" delete: tags: - "Account: Email" summary: Remove an email address description: | Used to remove an email address. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/Email" responses: "200": $ref: "#/components/responses/EmailAddresses" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_code: $ref: "#/components/examples/InvalidEmail" ###################################################################### # Account: Phone ###################################################################### /_allauth/{client}/v1/account/phone: get: tags: - "Account: Phone" summary: Get the phone number description: | Retrieves the phone number of the account, if any. Note that while the endpoint returns a list of phone numbers, at most one entry is returned. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" responses: "200": $ref: "#/components/responses/PhoneNumbers" "401": $ref: "#/components/responses/Authentication" post: tags: - "Account: Phone" summary: | Change the phone number description: | The following functionality is available: - Initiate the phone number change process for signed in users. - Change to a new phone number during the phone number verification process at signup for unauthenticated users. Note that this requires: `ACCOUNT_PHONE_VERIFICATION_SUPPORTS_CHANGE = True`. In both cases, after posting a new phone number, proceed with the phone verification endpoint to confirm the change of the phone number by posting the verification code. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/Phone" responses: "202": description: Phone number change process initiated. content: application/json: schema: $ref: "#/components/schemas/PhoneNumberChangeResponse" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" "401": $ref: "#/components/responses/AuthenticationOrReauthentication" "409": description: | Conflict. For example, when no user is authenticated and no phone verification flow is pending. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" ###################################################################### # Account: 2FA ###################################################################### /_allauth/{client}/v1/account/authenticators: get: tags: - "Account: 2FA" summary: List authenticators parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" responses: "200": $ref: "#/components/responses/Authenticators" "401": $ref: "#/components/responses/Authentication" "410": $ref: "#/components/responses/SessionGone" /_allauth/{client}/v1/account/authenticators/totp: get: tags: - "Account: 2FA" summary: TOTP authenticator status description: | Retrieve the information about the current TOTP authenticator, if any. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" responses: "404": $ref: "#/components/responses/TOTPAuthenticatorNotFound" "200": $ref: "#/components/responses/TOTPAuthenticator" "409": $ref: "#/components/responses/AddAuthenticatorConflict" post: tags: - "Account: 2FA" summary: Activate TOTP description: | The code should be provided from the consuming TOTP authenticator application which was generated using the TOTP authenticator secret retrieved from the TOTP authenticator status endpoint. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/SetupTOTP" responses: "200": $ref: "#/components/responses/TOTPAuthenticator" "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_code: $ref: "#/components/examples/InvalidAuthenticatorCode" "401": $ref: "#/components/responses/ReauthenticationRequired" "409": $ref: "#/components/responses/AddAuthenticatorConflict" delete: tags: - "Account: 2FA" summary: Deactivate TOTP description: | Deactivates TOTP authentication. If the user authentication is not sufficiently recent, a reauthentication flow (`401`) will is presented. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" responses: "200": $ref: "#/components/responses/StatusOK" "401": $ref: "#/components/responses/ReauthenticationRequired" /_allauth/{client}/v1/account/authenticators/recovery-codes: get: tags: - "Account: 2FA" summary: List recovery codes description: | List recovery codes. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" responses: "200": $ref: "#/components/responses/RecoveryCodes" "401": $ref: "#/components/responses/ReauthenticationRequired" "404": $ref: "#/components/responses/NotFound" post: tags: - "Account: 2FA" summary: Regenerate recovery codes parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" responses: "400": description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" examples: invalid_code: $ref: "#/components/examples/CannotGenerateRecoveryCodes" "401": $ref: "#/components/responses/ReauthenticationRequired" ###################################################################### # Account: WebAuthn ###################################################################### /_allauth/{client}/v1/account/authenticators/webauthn: get: tags: - "Account: WebAuthn" summary: | Get WebAuthn credential creation options parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" - $ref: "#/components/parameters/PasswordLess" description: | Returns the WebAuthn credential creation options, that can be processed using `parseCreationOptionsFromJSON()` on the frontend. responses: "200": $ref: "#/components/responses/WebAuthnCreationOptionsResponse" "401": $ref: "#/components/responses/ReauthenticationRequired" "409": $ref: "#/components/responses/AddAuthenticatorConflict" put: tags: - "Account: WebAuthn" summary: | Rename a WebAuthn credential description: | You can alter the name of a WebAuthn credential by PUT'ting the ID and name of the authenticator representing that credential. You can obtain the credentials via the "List authenticators" endpoint. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/UpdateWebAuthn" responses: "200": $ref: "#/components/responses/WebAuthnAuthenticator" "401": $ref: "#/components/responses/ReauthenticationRequired" delete: tags: - "Account: WebAuthn" summary: | Delete a WebAuthn credential parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/DeleteWebAuthn" responses: "200": $ref: "#/components/responses/StatusOK" "401": $ref: "#/components/responses/ReauthenticationRequired" post: tags: - "Account: WebAuthn" summary: | Add a WebAuthn credential parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/AddWebAuthnAuthenticator" responses: "200": $ref: "#/components/responses/AddWebAuthnAuthenticator" "401": $ref: "#/components/responses/ReauthenticationRequired" "409": $ref: "#/components/responses/AddAuthenticatorConflict" ###################################################################### # Sessions ###################################################################### /_allauth/{client}/v1/auth/session: get: tags: - "Authentication: Current Session" summary: | Get authentication status description: | Retrieve information about the authentication status for the current session. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" responses: "200": $ref: "#/components/responses/Authenticated" "401": $ref: "#/components/responses/Authentication" "410": $ref: "#/components/responses/SessionGone" delete: tags: - "Authentication: Current Session" summary: Logout description: | Logs out the user from the current session. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" responses: "401": $ref: "#/components/responses/Unauthenticated" ###################################################################### # Tokens ###################################################################### /_allauth/app/v1/tokens/refresh: post: tags: - "Tokens" summary: | Refresh the access token description: | Used to retrieve a new access token. Depending on `settings.HEADLESS_JWT_ROTATE_REFRESH_TOKEN`, a new refresh token is returned as well. requestBody: $ref: "#/components/requestBodies/RefreshToken" responses: "200": $ref: "#/components/responses/RefreshToken" "400": description: The refresh token is invalid or expired. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" ###################################################################### # Account: Password ###################################################################### /_allauth/{client}/v1/account/password/change: post: tags: - "Account: Password" summary: Change password description: | In order to change the password of an account, the current and new password must be provider. However, accounts that were created by signing up using a third-party provider do not have a password set. In that case, the current password is not required. parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/ChangePassword" responses: "400": $ref: "#/components/responses/Error" "401": $ref: "#/components/responses/Authentication" ###################################################################### # Authentication: Current Sessions ###################################################################### /_allauth/{client}/v1/auth/sessions: get: tags: - "Sessions" summary: List sessions parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" responses: "200": $ref: "#/components/responses/Sessions" delete: tags: - "Sessions" summary: End one or more sessions parameters: - $ref: "#/components/parameters/Client" - $ref: "#/components/parameters/SessionToken" requestBody: $ref: "#/components/requestBodies/EndSessions" responses: "200": $ref: "#/components/responses/Sessions" "401": $ref: "#/components/responses/Authentication" components: ###################################################################### # Components: Examples ###################################################################### examples: User: value: &user-example id: 123 display: Magic Wizard has_usable_password: true email: email@domain.org username: wizard AuthenticatedByPassword: summary: | Authenticated by password. value: status: 200 data: user: *user-example methods: - method: password at: 1711555057.065702 email: email@domain.org meta: is_authenticated: true session_token: ufwcig0zen9skyd545jc0fkq813ghar2 access_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdW AuthenticatedByCode: summary: | Authenticated by code. value: status: 200 data: user: *user-example methods: - method: code at: 1711555057.065702 email: email@domain.org meta: is_authenticated: true session_token: ufwcig0zen9skyd545jc0fkq813ghar2 access_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdW AuthenticatedByPasswordAnd2FA: summary: | Fully authenticated using by password and 2FA. value: status: 200 data: user: *user-example methods: - method: password at: 1711555057.065702 email: email@domain.org - method: mfa at: 1711555060.9375854 id: 66 type: totp meta: is_authenticated: true session_token: ufwcig0zen9skyd545jc0fkq813ghar2 access_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdW CannotGenerateRecoveryCodes: summary: | Unable to generate recovery codes. value: status: 400 errors: - message: | You cannot deactivate two-factor authentication. code: cannot_generate_recovery_codes DisconnectNotAllowedNoPassword: summary: Account without a password. value: status: 400 errors: - message: Your account has no password set up. code: no_password param: account DisconnectNotAllowedNoVerifiedEmail: summary: Account without a verified email. value: status: 400 errors: - message: Your account has no verified email address. code: no_verified_email param: account InvalidAuthenticatorCode: summary: | An error response indicating that the provided code is incorrect. value: status: 400 errors: - message: Incorrect code. code: incorrect_code param: code InvalidEmailVerificationKey: summary: | Email verification key invalid. value: status: 400 errors: - message: Invalid or expired key. code: invalid param: key InvalidEmail: value: status: 400 errors: - message: Enter a valid email address. code: invalid param: email IncorrectPassword: value: status: 400 errors: - message: Incorrect password. param: password code: incorrect_password InvalidPasswordResetKey: summary: | Password reset key invalid. value: status: 400 errors: - message: The password reset token was invalid. code: token_invalid param: key InvalidProviderToken: summary: | Provider token invalid. value: status: 400 errors: - message: The token was invalid. code: invalid param: token PasswordMismatch: value: status: 400 errors: - message: The email address and/or password you specified are not correct. code: email_password_mismatch param: password UnauthenticatedInitial: summary: | Unauthenticated: Initial value: status: 401 data: flows: - id: login - id: signup - id: provider_redirect providers: - facebook - google - telegram - id: provider_token providers: - google meta: is_authenticated: false UnauthenticatedPending2FA: summary: | Unauthenticated: pending 2FA value: status: 401 data: flows: - id: login - id: signup - id: provider_redirect providers: - facebook - google - telegram - id: provider_token providers: - google - id: mfa_authenticate is_pending: true meta: is_authenticated: false UnauthenticatedPendingLoginByCode: summary: | Unauthenticated: pending login by code value: status: 401 data: flows: - id: login - id: signup - id: provider_redirect providers: - facebook - google - telegram - id: provider_token providers: - google - id: mfa_authenticate - id: login_by_code is_pending: true meta: is_authenticated: false UnauthenticatedPendingProviderSignup: summary: | Unauthenticated: pending provider signup value: status: 401 data: flows: - id: login - id: signup - id: provider_redirect providers: - facebook - google - telegram - id: provider_token providers: - google - id: provider_signup provider: id: google name: Google client_id: 123.apps.googleusercontent.com flows: - provider_redirect - provider_token is_pending: true meta: is_authenticated: false UnauthenticatedPendingEmailVerification: summary: | Unauthenticated: pending email verification value: status: 401 data: flows: - id: login - id: signup - id: provider_redirect providers: - facebook - google - telegram - id: provider_token providers: - google - id: verify_email is_pending: true meta: is_authenticated: false ReauthenticationRequired: summary: | Reauthentication required value: status: 401 data: user: *user-example methods: - method: password at: 1711555057.065702 email: email@domain.org - method: mfa at: 1711555060.9375854 id: 66 type: totp flows: - id: reauthenticate - id: mfa_reauthenticate meta: is_authenticated: true ###################################################################### # Components: Request bodies ###################################################################### requestBodies: Login: description: Login. required: true content: application/json: schema: $ref: "#/components/schemas/Login" LoginWebAuthn: description: Login using WebAuthn. required: true content: application/json: schema: type: object properties: credential: $ref: "#/components/schemas/WebAuthnCredential" required: - credential ReauthenticateWebAuthn: description: Reauthenticate using WebAuthn. required: true content: application/json: schema: type: object properties: credential: $ref: "#/components/schemas/WebAuthnCredential" required: - credential AuthenticateWebAuthn: description: Authenticate using WebAuthn. required: true content: application/json: schema: type: object properties: credential: $ref: "#/components/schemas/WebAuthnCredential" required: - credential MFAAuthenticate: required: true content: application/json: schema: $ref: "#/components/schemas/MFAAuthenticate" MFATrust: required: true content: application/json: schema: $ref: "#/components/schemas/MFATrust" ConfirmLoginCode: required: true content: application/json: schema: $ref: "#/components/schemas/ConfirmLoginCode" EndSessions: required: true content: application/json: schema: $ref: "#/components/schemas/EndSessions" PasskeySignup: description: Signup using a passkey required: true content: application/json: schema: $ref: "#/components/schemas/PasskeySignup" ProviderAccount: content: application/json: schema: type: object properties: provider: $ref: "#/components/schemas/ProviderID" account: $ref: "#/components/schemas/ProviderAccountID" required: - account - provider ProviderRedirect: required: true description: | Initiate the provider redirect flow. content: application/x-www-form-urlencoded: schema: $ref: "#/components/schemas/ProviderRedirect" ProviderSignup: description: Provider signup. required: true content: application/json: schema: $ref: "#/components/schemas/ProviderSignup" ProviderToken: required: true content: application/json: schema: $ref: "#/components/schemas/ProviderToken" Reauthenticate: description: Reauthenticate. required: true content: application/json: schema: $ref: "#/components/schemas/Reauthenticate" RefreshToken: content: application/json: schema: type: object properties: refresh_token: $ref: "#/components/schemas/RefreshToken" required: - refresh_token RequestPassword: description: Request password. required: true content: application/json: schema: $ref: "#/components/schemas/RequestPassword" RequestLoginCode: description: Request a login code. required: true content: application/json: schema: $ref: "#/components/schemas/RequestLoginCode" SetupTOTP: content: application/json: schema: type: object properties: code: $ref: "#/components/schemas/AuthenticatorCode" required: - code Signup: description: Signup required: true content: application/json: schema: $ref: "#/components/schemas/Signup" ChangePassword: content: application/json: schema: type: object properties: current_password: $ref: "#/components/schemas/Password" new_password: type: string description: | The current password. example: Aberto! required: - new_password Email: content: application/json: schema: type: object properties: email: $ref: '#/components/schemas/Email' required: - email MarkPrimaryEmail: content: application/json: schema: type: object properties: email: type: string description: | An email address. example: email@domain.org primary: type: boolean enum: - true description: | Primary flag. required: - email - primary Phone: content: application/json: schema: type: object properties: phone: type: string example: "+314159265359" required: - phone ResetPassword: content: application/json: schema: $ref: "#/components/schemas/ResetPassword" VerifyEmail: content: application/json: schema: $ref: "#/components/schemas/VerifyEmail" VerifyPhone: content: application/json: schema: $ref: "#/components/schemas/VerifyPhone" UpdateWebAuthn: content: application/json: schema: type: object properties: id: $ref: "#/components/schemas/AuthenticatorID" name: type: string example: "Master key" AddWebAuthnAuthenticator: content: application/json: schema: type: object properties: name: type: string example: "Master key" credential: $ref: "#/components/schemas/WebAuthnCredential" required: - credential DeleteWebAuthn: content: application/json: schema: type: object properties: authenticators: description: | The IDs of the authenticator that are to be deleted. type: array items: $ref: "#/components/schemas/AuthenticatorID" required: - authenticators ###################################################################### # Components: Schemas ###################################################################### schemas: Session: type: object properties: user_agent: type: string example: Mozilla Firefox ip: type: string example: 127.2.3.192 created_at: $ref: "#/components/schemas/Timestamp" is_current: type: boolean id: type: integer example: 123 last_seen_at: $ref: "#/components/schemas/Timestamp" required: - user_agent - ip - created_at - is_current - id AccountConfiguration: type: object description: | Configuration of the Django `allauth.account` app. properties: login_methods: type: array items: type: string enum: - email - username is_open_for_signup: type: boolean email_verification_by_code_enabled: type: boolean login_by_code_enabled: type: boolean password_reset_by_code_enabled: type: boolean required: - authentication_method - email_verification_by_code_enabled - is_open_for_signup - login_by_code_enabled AuthenticationResponse: type: object description: | An authentication related response. properties: status: type: integer enum: - 401 data: type: object properties: flows: type: array items: $ref: "#/components/schemas/Flow" required: - flows meta: $ref: "#/components/schemas/AuthenticationMeta" required: - status - data - meta ForbiddenResponse: type: object properties: status: type: integer enum: - 403 required: - status ConflictResponse: type: object properties: status: type: integer enum: - 409 required: - status EndSessions: type: object properties: sessions: description: | The IDs of the sessions that are to be ended. type: array items: type: integer example: 123 required: - sessions PhoneNumber: type: object description: | A phone number. properties: phone: type: string example: "+314159265359" verified: type: boolean required: - phone - verified PhoneNumbersResponse: type: object properties: status: $ref: "#/components/schemas/StatusOK" data: type: array items: $ref: "#/components/schemas/PhoneNumber" required: - status - data PhoneNumberChangeResponse: type: object properties: status: $ref: "#/components/schemas/StatusAccepted" data: type: array items: $ref: "#/components/schemas/PhoneNumber" required: - status - data example: status: 202 data: - phone: "+314159265359" verified: false ReauthenticationResponse: type: object description: | A response indicating reauthentication is required. properties: status: type: integer enum: - 401 data: $ref: "#/components/schemas/ReauthenticationRequired" meta: $ref: "#/components/schemas/AuthenticatedMeta" required: - status - data - meta SessionGoneResponse: type: object description: | The session is expired or invalid. properties: status: type: integer enum: - 410 data: type: object meta: $ref: "#/components/schemas/AuthenticationMeta" required: - status - data - meta BaseAuthenticationMeta: type: object properties: session_token: type: string description: | The session token (`app` clients only). example: ufwcig0zen9skyd545jc0fkq813ghar2 access_token: type: string description: | The access token (`app` clients only). example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdW AuthenticationMeta: allOf: - $ref: "#/components/schemas/BaseAuthenticationMeta" - type: object description: | Metadata available in an authentication related response. properties: is_authenticated: type: boolean required: - is_authenticated AuthenticatedMeta: allOf: - $ref: "#/components/schemas/BaseAuthenticationMeta" - type: object description: | Metadata available in an re-authentication related response. properties: is_authenticated: type: boolean enum: - true required: - is_authenticated Flow: type: object properties: id: type: string enum: - login - login_by_code - mfa_authenticate - mfa_reauthenticate - provider_redirect - provider_signup - provider_token - reauthenticate - signup - verify_email - verify_phone provider: $ref: "#/components/schemas/Provider" is_pending: type: boolean enum: - true types: type: array description: Matches `settings.MFA_SUPPORTED_TYPES`. items: $ref: "#/components/schemas/AuthenticatorType" required: - id Authenticated: type: object properties: user: $ref: "#/components/schemas/User" methods: type: array description: | A list of methods used to authenticate. items: $ref: "#/components/schemas/AuthenticationMethod" required: - user - methods ReauthenticationRequired: properties: flows: type: array items: $ref: '#/components/schemas/Flow' user: $ref: '#/components/schemas/User' methods: type: array description: | A list of methods used to authenticate. items: $ref: '#/components/schemas/AuthenticationMethod' required: - flows - user - methods type: object AuthenticationMethod: oneOf: - type: object title: | Authenticated by username/email login properties: method: type: string enum: - password at: $ref: "#/components/schemas/Timestamp" email: $ref: "#/components/schemas/Email" username: $ref: "#/components/schemas/Username" required: - method - at - type: object title: | Authenticated after password reset properties: method: type: string enum: - password_reset at: $ref: "#/components/schemas/Timestamp" email: $ref: "#/components/schemas/Email" required: - at - email - method - type: object title: | Authenticated by confirming a code sent by email. properties: method: type: string enum: - code at: $ref: "#/components/schemas/Timestamp" email: $ref: "#/components/schemas/Email" required: - at - email - method - type: object title: | Authenticated by confirming a code sent by phone. properties: method: type: string enum: - code at: $ref: "#/components/schemas/Timestamp" phone: $ref: "#/components/schemas/Phone" required: - at - method - phone - type: object title: | Reauthenticated by password properties: method: type: string enum: - password at: $ref: "#/components/schemas/Timestamp" reauthenticated: type: boolean enum: - true required: - method - reauthenticated - at - type: object title: | Authenticated by third-party provider properties: method: type: string enum: - socialaccount at: $ref: "#/components/schemas/Timestamp" provider: $ref: "#/components/schemas/ProviderID" uid: $ref: "#/components/schemas/ProviderAccountID" required: - method - reauthenticated - at - provider - uid - type: object title: | (Re)authenticated by 2FA properties: method: type: string enum: - mfa at: $ref: "#/components/schemas/Timestamp" type: $ref: "#/components/schemas/AuthenticatorType" reauthenticated: type: boolean required: - method - at - type AuthenticatedResponse: type: object properties: status: $ref: "#/components/schemas/StatusOK" data: $ref: "#/components/schemas/Authenticated" meta: $ref: "#/components/schemas/AuthenticationMeta" required: - status - data - meta MFAAuthenticate: type: object properties: code: $ref: "#/components/schemas/AuthenticatorCode" required: - code MFATrust: type: object properties: trust: type: boolean required: - trust ConfirmLoginCode: type: object properties: code: $ref: "#/components/schemas/Code" required: - code ClientID: type: string description: | The client ID (in case of OAuth2 or OpenID Connect based providers) example: 123.apps.googleusercontent.com ProviderToken: type: object properties: provider: $ref: "#/components/schemas/ProviderID" process: $ref: "#/components/schemas/Process" token: description: | The token. type: object properties: client_id: $ref: "#/components/schemas/ClientID" id_token: type: string description: | The ID token. example: eyJhbGciOiJI access_token: type: string description: | The access token. example: 36POk6yJV_adQs required: - client_id required: - provider - process - token ProviderRedirect: type: object properties: provider: $ref: "#/components/schemas/ProviderID" callback_url: type: string description: | The URL to return to after the redirect flow is complete. Note that this is not to be mistaken with the callback URL that you configure over at the OAuth provider during the OAuth app/client setup. The flow is as follows: 1. Your frontend redirects to the headless provider redirect endpoint in a synchronous (non-XHR) manner, informing allauth (by means of `callback_url`) where to redirect to after the provider handshake is completed. 2. Headless will redirect to the (OAuth) identity provider to initiate the handshake, passing along a different callback URL to the provider: one that points to an allauth backend URL. This is the URL that you need to have setup at your OAuth app/client configuration. Note that this must be a backend URL as providers can use POST requests to perform their callbacks, which is something a frontend would not be able to handle. 3. After the authorization at the provider is completed, the provider redirects to the *backend* allauth callback URL, which will then redirect back to the *frontend* callback URL. 4. Your frontend is now expected to fetch the current session to determine what the next course of action is. The user could be authenticated at this point, or another flow is pending (e.g. email verification, or, provider signup). In case of errors a `?error=` is passed to the frontend callback URL. example: https://app.project.org/account/provider/callback process: $ref: "#/components/schemas/Process" required: - provider - process - callback_url RequestPassword: type: object properties: email: $ref: "#/components/schemas/Email" required: - email RequestLoginCode: type: object anyOf: - title: Request login code (phone) properties: phone: $ref: "#/components/schemas/Phone" required: - phone - title: Request login code (email) properties: email: $ref: "#/components/schemas/Email" required: - email Reauthenticate: type: object properties: password: $ref: "#/components/schemas/Password" required: - password ProviderSignup: allOf: - $ref: '#/components/schemas/BaseSignup' PasskeySignup: allOf: - $ref: '#/components/schemas/BaseSignup' BaseSignup: type: object properties: email: $ref: "#/components/schemas/Email" phone: $ref: "#/components/schemas/Phone" username: $ref: "#/components/schemas/Username" Signup: allOf: - $ref: '#/components/schemas/BaseSignup' - type: object properties: password: $ref: "#/components/schemas/Password" required: - password Username: type: string description: | The username. example: wizard Email: type: string description: | The email address. example: email@domain.org Phone: type: string description: | The phone number. example: "+314159265359" AccessToken: type: string description: | The access token. example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdW RefreshToken: type: string description: | The refresh token. example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.QV30 Login: allOf: - type: object properties: password: $ref: "#/components/schemas/Password" required: - password - anyOf: - title: Login by username properties: username: $ref: "#/components/schemas/Username" required: - username - title: Login by email properties: email: $ref: "#/components/schemas/Email" required: - email - title: Login by phone properties: phone: $ref: "#/components/schemas/Phone" required: - phone StatusOK: type: integer enum: - 200 StatusAccepted: type: integer enum: - 202 AuthenticatorID: type: integer description: | Authenticator ID. example: 123 SocialAccountConfiguration: type: object description: | Configuration of the Django `allauth.socialaccount` app. properties: providers: $ref: "#/components/schemas/ProviderList" required: - providers MFAConfiguration: type: object description: | Configuration of the Django `allauth.mfa` app. properties: supported_types: type: array description: | Matches `settings.MFA_SUPPORTED_TYPES`. items: $ref: "#/components/schemas/AuthenticatorType" required: - supported_types UserSessionsConfiguration: type: object description: | Configuration of the Django `allauth.usersessions` app. properties: track_activity: type: boolean description: | Matches `settings.USERSESSIONS_TRACK_ACTIVITY`. required: - track_activity ConfigurationResponse: type: object properties: data: type: object properties: account: $ref: "#/components/schemas/AccountConfiguration" socialaccount: $ref: "#/components/schemas/SocialAccountConfiguration" mfa: $ref: "#/components/schemas/MFAConfiguration" usersessions: $ref: "#/components/schemas/UserSessionsConfiguration" required: - account status: $ref: "#/components/schemas/StatusOK" required: - status - data example: status: 200 data: account: authentication_method: email socialaccount: providers: - id: "google" name: "Google" flows: - "provider_redirect" - "provider_token" client_id: "123.apps.googleusercontent.com" openid_configuration_url: https://accounts.google.com/.well-known/openid-configuration mfa: supported_types: - "recovery_codes" - "totp" usersessions: track_activity: false ResetPassword: type: object properties: key: type: string description: The password reset key example: 2f-c4nqd4-e07d9bc694f9f28cd4fe92569d495333 password: $ref: "#/components/schemas/Password" required: - key - password VerifyEmail: type: object properties: key: type: string description: The email verification key example: 2f-c4nqd4-e07d9bc694f9f28cd4fe92569d495333 required: - key VerifyPhone: type: object properties: code: type: string description: The phone verification code example: 4S3H82 required: - code OptionalTimestamp: nullable: true $ref: "#/components/schemas/Timestamp" Timestamp: type: number description: | An epoch based timestamp (trivial to parse using: `new Date(value)*1000`) example: 1711555057.065702 AuthenticatorCode: type: string description: | An authenticator code. example: "314159" Code: type: string description: | An one-time code. example: "NQ3TM5" AuthenticatorType: type: string enum: - recovery_codes - totp - webauthn description: | The type of authenticator. Password: type: string description: | The password. example: Alohomora! ErrorResponse: type: object properties: status: type: integer enum: - 400 example: 400 errors: type: array items: type: object properties: code: type: string example: invalid description: | An error code. param: type: string example: email description: | The name of the input parameter that was incorrect. message: type: string example: Enter a valid email address. description: | A human readable error message. required: - code - message Process: type: string description: | The process to be executed when the user successfully authenticates. When set to `login`, the user will be logged into the account to which the provider account is connected, or if no such account exists, a signup will occur. If set to `connect`, the provider account will be connected to the list of provider accounts for the currently authenticated user. enum: - login - connect example: login ProviderID: type: string description: | The provider ID. example: google ProviderAccountID: type: string description: | The provider specific account ID. example: goo12345 User: type: object properties: id: description: | The user ID. oneOf: - type: integer example: 123 - type: string example: 89d3f9a0-51a5-49dd-8b97-7536641958e9 display: type: string description: | The display name for the user. example: Magic Wizard has_usable_password: type: boolean description: | Whether or not the account has a password set. example: true email: $ref: "#/components/schemas/Email" username: $ref: "#/components/schemas/Username" EmailAddress: type: object properties: email: $ref: "#/components/schemas/Email" primary: type: boolean example: true verified: type: boolean example: false required: - email - primary - verified BaseAuthenticator: type: object properties: last_used_at: $ref: "#/components/schemas/OptionalTimestamp" created_at: $ref: "#/components/schemas/Timestamp" required: - created_at - last_used_at TOTPAuthenticator: allOf: - $ref: "#/components/schemas/BaseAuthenticator" - type: object properties: type: type: string enum: - totp required: - type WebAuthnAuthenticator: allOf: - $ref: "#/components/schemas/BaseAuthenticator" - type: object properties: type: type: string enum: - webauthn id: $ref: "#/components/schemas/AuthenticatorID" name: type: string example: "Master key" is_passwordless: type: boolean description: | Whether or not this authenticator represents a passkey. Absent if it is not specified. required: - type - id - name RecoveryCodesAuthenticator: allOf: - $ref: "#/components/schemas/BaseAuthenticator" - type: object properties: type: type: string description: | The authenticator type. enum: - recovery_codes total_code_count: type: integer description: | The total number of recovery codes that initially were available. example: 10 unused_code_count: type: integer description: | The number of recovery codes that are unused. example: 7 required: - type - total_code_count - unused_code_count SensitiveRecoveryCodesAuthenticator: allOf: - $ref: "#/components/schemas/RecoveryCodesAuthenticator" - type: object properties: unused_codes: type: array description: | The list of unused codes. items: $ref: "#/components/schemas/AuthenticatorCode" required: - unused_codes AuthenticatorList: type: array items: oneOf: - $ref: "#/components/schemas/TOTPAuthenticator" - $ref: "#/components/schemas/RecoveryCodesAuthenticator" - $ref: "#/components/schemas/WebAuthnAuthenticator" ProviderList: type: array items: $ref: "#/components/schemas/Provider" Provider: type: object properties: id: type: string example: google description: | The provider ID. name: type: string description: | The name of the provider. example: Google client_id: type: string description: | The client ID (in case of OAuth2 or OpenID Connect based providers) example: 123.apps.googleusercontent.com openid_configuration_url: type: string description: | The OIDC discovery or well-known URL (in case of OAuth2 or OpenID Connect based providers) example: https://accounts.google.com/.well-known/openid-configuration flows: type: array description: | The authentication flows the provider integration supports. items: type: string enum: - provider_redirect - provider_token required: - id - name - flows ProviderAccount: type: object properties: uid: $ref: "#/components/schemas/ProviderAccountID" display: type: string description: | A name derived from the third-party provider account data. example: Wizzkid provider: $ref: "#/components/schemas/Provider" required: - uid - provider - display EmailVerificationInfo: type: object properties: status: $ref: "#/components/schemas/StatusOK" data: type: object properties: email: $ref: "#/components/schemas/Email" user: $ref: "#/components/schemas/User" required: - email - user meta: type: object properties: is_authenticating: type: boolean required: - is_authenticating required: - status - data - meta WebAuthnCredentialRequestOptions: type: object properties: request_options: type: object example: {"status": 200, "data": {"request_options": {"publicKey": {"challenge": "aOecJJtLA2e-Dj2WU-zbRoJewbQqSUPxoA9EzsUL72o", "rpId": "localhost", "allowCredentials": [], "userVerification": "preferred"}}}} required: - request_options WebAuthnCredentialCreationOptions: type: object properties: creation_options: type: object example: {"status": 200, "data": {"request_options": {"publicKey": {"challenge": "aOecJJtLA2e-Dj2WU-zbRoJewbQqSUPxoA9EzsUL72o", "rpId": "localhost", "allowCredentials": [], "userVerification": "preferred"}}}} required: - creation_options WebAuthnCredential: type: object example: {"credential":{"type":"public-key","id":"-J4JNfPfnLyRSMK4R...","rawId":"-J4JNfPfnLyRSMK4R...","authenticatorAttachment":"cross-platform","response":{"clientDataJSON":"eyJjaGFsbGVuZ2UiOi...","authenticatorData":"SZYN5YgO...","signature":"MEUCIE-7sqILygPqGbrRZ4j2nqeqUU...","userHandle":"Mg..."},"clientExtensionResults":{}}} ###################################################################### # Components: Parameters ###################################################################### parameters: Client: name: client in: path description: The type of client accessing the API. required: true schema: type: string enum: - app - browser EmailVerificationKey: in: header name: X-Email-Verification-Key schema: type: string required: true description: The email verification key PasswordResetKey: in: header name: X-Password-Reset-Key schema: type: string required: true description: The password reset key SessionToken: in: header name: X-Session-Token required: false description: | Session token. Only needed when `client` is equal to `app`. schema: type: string PasswordLess: in: query name: passwordless required: false schema: type: boolean allowEmptyValue: true description: | When present (regardless of its value), enables passwordless sign-in via a WebAuthn credential (Passkey), but may enforce additional multi-factor authentication (MFA) requirements. Omit the parameter to disable. ###################################################################### # Components: Responses ###################################################################### responses: AddAuthenticatorConflict: description: | The account prohibits adding an authenticator, e.g. because of an unverified email address. content: application/json: schema: $ref: "#/components/schemas/ConflictResponse" Authentication: description: Not authenticated. content: application/json: schema: $ref: "#/components/schemas/AuthenticationResponse" examples: unauthenticated_initial: $ref: "#/components/examples/UnauthenticatedInitial" unauthenticated_pending_2fa: $ref: "#/components/examples/UnauthenticatedPending2FA" unauthenticated_pending_provider_signup: $ref: "#/components/examples/UnauthenticatedPendingProviderSignup" unauthenticated_pending_email_verification: $ref: "#/components/examples/UnauthenticatedPendingEmailVerification" reauthentication_required: $ref: "#/components/examples/ReauthenticationRequired" Authenticators: description: | List of authenticators. content: application/json: schema: type: object properties: status: $ref: "#/components/schemas/StatusOK" data: $ref: "#/components/schemas/AuthenticatorList" required: - status - data AuthenticatedByPassword: description: | Authenticated by password. content: application/json: schema: $ref: "#/components/schemas/AuthenticatedResponse" examples: authenticated: $ref: "#/components/examples/AuthenticatedByPassword" AuthenticatedByCode: description: | Authenticated by code. content: application/json: schema: $ref: "#/components/schemas/AuthenticatedResponse" examples: authenticated: $ref: "#/components/examples/AuthenticatedByCode" AuthenticatedByPasswordAnd2FA: description: | Authenticated by password and 2FA. content: application/json: schema: $ref: "#/components/schemas/AuthenticatedResponse" examples: authenticated: $ref: "#/components/examples/AuthenticatedByPasswordAnd2FA" AuthenticationOrReauthentication: description: | The response indicates authentication or re-authentication is required. content: application/json: schema: oneOf: - $ref: "#/components/schemas/AuthenticationResponse" - $ref: "#/components/schemas/ReauthenticationResponse" Configuration: description: | The django-allauth configuration. content: application/json: schema: $ref: "#/components/schemas/ConfigurationResponse" EmailAddresses: description: | List of email addresses. content: application/json: schema: type: object properties: status: $ref: "#/components/schemas/StatusOK" data: type: array items: $ref: "#/components/schemas/EmailAddress" required: - status - data EmailVerificationInfo: description: Email verification information. content: application/json: schema: $ref: "#/components/schemas/EmailVerificationInfo" Error: description: An input error occurred. content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" Forbidden: description: | A forbidden response. content: application/json: schema: $ref: "#/components/schemas/ForbiddenResponse" NotFound: description: | Not found. content: application/json: schema: type: object properties: status: type: integer enum: - 404 required: - status PasswordResetInfo: description: Information about the password reset key. content: application/json: schema: type: object properties: status: $ref: "#/components/schemas/StatusOK" data: type: object properties: user: $ref: "#/components/schemas/User" required: - status - data PhoneNumbers: description: | List of phone numbers. content: application/json: schema: $ref: "#/components/schemas/PhoneNumbersResponse" ProviderAccounts: description: | List of third-party provider accounts. content: application/json: schema: type: object properties: status: $ref: "#/components/schemas/StatusOK" data: type: array items: $ref: "#/components/schemas/ProviderAccount" required: - status - data ProviderSignup: description: | Information relating to the pending provider signup. content: application/json: schema: type: object properties: status: $ref: "#/components/schemas/StatusOK" data: type: object properties: email: type: array items: $ref: "#/components/schemas/EmailAddress" account: $ref: "#/components/schemas/ProviderAccount" user: $ref: "#/components/schemas/User" required: - email - account - user required: - status - data ReauthenticationRequired: description: | The response indicates reauthentication is required. content: application/json: schema: $ref: "#/components/schemas/ReauthenticationResponse" examples: reauthentication_required: summary: | Reauthentication required value: status: 401 data: user: *user-example methods: - method: password at: 1711555057.065702 email: email@domain.org - method: mfa at: 1711555060.9375854 id: 66 type: totp flows: - id: reauthenticate - id: mfa_reauthenticate meta: is_authenticated: true RecoveryCodes: description: | Information on the recovery codes. content: application/json: schema: type: object properties: status: $ref: "#/components/schemas/StatusOK" data: $ref: "#/components/schemas/SensitiveRecoveryCodesAuthenticator" required: - status - data RefreshToken: description: A new access token (and optionally new refresh token). content: application/json: schema: type: object properties: status: $ref: "#/components/schemas/StatusOK" data: type: object properties: access_token: $ref: "#/components/schemas/AccessToken" refresh_token: $ref: "#/components/schemas/RefreshToken" required: - access_token required: - data - status Sessions: description: | List of sessions. content: application/json: schema: type: object properties: status: $ref: "#/components/schemas/StatusOK" data: type: array items: $ref: "#/components/schemas/Session" required: - status - data SessionGone: description: | The response indicates session is invalid or no longer exists. content: application/json: schema: $ref: "#/components/schemas/SessionGoneResponse" examples: unauth: $ref: "#/components/examples/UnauthenticatedInitial" StatusOK: description: | A success response. content: application/json: schema: type: object properties: status: $ref: "#/components/schemas/StatusOK" required: - status TooManyRequests: description: | Too many requests. content: application/json: schema: type: object properties: status: type: integer enum: - 429 required: - status TOTPAuthenticator: description: | Information on the TOTP authenticator. content: application/json: schema: type: object properties: status: $ref: "#/components/schemas/StatusOK" meta: properties: recovery_codes_generated: description: Whether or not recovery codes where generated automatically. type: boolean type: object data: $ref: "#/components/schemas/TOTPAuthenticator" required: - status - data TOTPAuthenticatorNotFound: description: | No TOTP authenticator has been set up. content: application/json: schema: type: object properties: status: type: integer enum: - 404 meta: type: object properties: secret: type: string description: | A TOTP secret that can be used to setup a new authenticator. example: J4ZKKXTK7NOVU7EPUVY23LCDV4T2QZYM totp_url: type: string description: | otpauth URI from which a QR code can be generated and scanned by OTP clients. example: otpauth://totp/Example:alice@fsf.org?secret=JBSWY3DPEHPK3PXP&issuer=Example required: - secret - totp_url required: - status - meta Unauthenticated: description: | There is no authenticated session. content: application/json: schema: $ref: "#/components/schemas/AuthenticationResponse" examples: unauth: $ref: "#/components/examples/UnauthenticatedInitial" Authenticated: description: The user is authenticated. content: application/json: schema: $ref: "#/components/schemas/AuthenticatedResponse" WebAuthnRequestOptionsResponse: description: WebAuthn credential request options. content: application/json: schema: type: object properties: status: $ref: "#/components/schemas/StatusOK" data: $ref: "#/components/schemas/WebAuthnCredentialRequestOptions" required: - status - data WebAuthnCreationOptionsResponse: description: WebAuthn credential creation options. content: application/json: schema: type: object properties: status: $ref: "#/components/schemas/StatusOK" data: $ref: "#/components/schemas/WebAuthnCredentialCreationOptions" required: - status - data WebAuthnAuthenticator: description: A WebAuthn authenticator. content: application/json: schema: type: object properties: status: $ref: "#/components/schemas/StatusOK" data: $ref: "#/components/schemas/WebAuthnAuthenticator" required: - status - data AddWebAuthnAuthenticator: description: A WebAuthn authenticator. content: application/json: schema: type: object properties: status: $ref: "#/components/schemas/StatusOK" data: $ref: "#/components/schemas/WebAuthnAuthenticator" meta: type: object properties: recovery_codes_generated: type: boolean description: | Whether or not recovery codes where generated automatically. required: - status - data - meta ================================================ FILE: allauth/headless/spec/internal/__init__.py ================================================ ================================================ FILE: allauth/headless/spec/internal/openapikit.py ================================================ import dataclasses import datetime from typing import Any, Union, get_args, get_origin from django import forms from allauth.account.fields import EmailField FIELD_MAPPING = { forms.CharField: {"type": "string"}, forms.IntegerField: {"type": "integer"}, forms.FloatField: {"type": "number"}, forms.BooleanField: {"type": "boolean"}, forms.DateField: {"type": "string", "format": "date"}, forms.DateTimeField: {"type": "string", "format": "date-time"}, forms.EmailField: {"type": "string", "format": "email"}, EmailField: {"type": "string", "format": "email"}, forms.URLField: {"type": "string", "format": "uri"}, forms.DecimalField: { "type": "string", "format": "decimal", "pattern": r"^\d+(\.\d+)?$", }, } def spec_for_field(field: forms.Field) -> dict[str, Any]: field_spec: dict[str, Any] = FIELD_MAPPING.get(type(field), {"type": "string"}) field_spec = dict(field_spec) if hasattr(field, "max_length") and field.max_length: field_spec["maxLength"] = field.max_length if hasattr(field, "min_length") and field.min_length: field_spec["minLength"] = field.min_length if hasattr(field, "help_text") and field.help_text: field_spec["description"] = field.help_text return field_spec def unwrap_optional_type(typ): if get_origin(typ) is not Union: return typ, True args = get_args(typ) if len(args) != 2 or type(None) not in args: return typ, True return args[0], False def spec_for_dataclass(dc) -> tuple[dict, dict]: example = {} props = {} required = [] for field_id, field in dc.__dataclass_fields__.items(): descriptor = {} if description := field.metadata.get("description"): descriptor["description"] = description if exa := field.metadata.get("example"): descriptor["example"] = exa example[field_id] = exa field_type, req = unwrap_optional_type(field.type) if req: required.append(field_id) if field_type is str: descriptor.update({"type": "string"}) elif field_type is int: descriptor.update({"type": "integer"}) elif field_type is float: descriptor.update({"type": "number", "format": "float"}) elif field_type is bool: descriptor.update({"type": "boolean"}) elif field_type is datetime.datetime: descriptor.update({"type": "string", "format": "date-time"}) elif field_type is datetime.date: descriptor.update({"type": "string", "format": "date"}) elif field_type is list: descriptor.update({"type": "array"}) elif field_type is dict: descriptor.update({"type": "object"}) elif dataclasses.is_dataclass(field_type): nested_schema, nested_exmple = spec_for_dataclass(field_type) descriptor.update(nested_schema) descriptor["example"] = nested_exmple else: descriptor.update({"type": "string"}) props[field_id] = descriptor schema = {"type": "object", "properties": props, "required": required} return schema, example ================================================ FILE: allauth/headless/spec/internal/schema.py ================================================ from pathlib import Path from django.urls import reverse from django.urls.exceptions import Resolver404 from allauth.account import app_settings as account_settings from allauth.account.internal.flows.signup import base_signup_form_class from allauth.core.internal.urlkit import script_aware_resolve from allauth.headless import app_settings from allauth.headless.adapter import get_adapter from allauth.headless.spec.internal.openapikit import spec_for_dataclass, spec_for_field def get_schema() -> dict: import yaml path = Path(__file__).parent.parent / "doc/openapi.yaml" with open(path, "rb") as f: spec = yaml.safe_load(f) path = Path(__file__).parent.parent / "doc/description.md" with open(path, "rb") as f: description = f.read().decode("utf8") spec["info"]["description"] = description specify_user(spec) chroot(spec) pin_client(spec) drop_unused_client_parameter(spec) used_tags = drop_unused_paths(spec) drop_unused_tags(spec, used_tags) drop_unused_tag_groups(spec, used_tags) specify_signup_fields(spec) specify_custom_signup_form(spec) return spec def chroot(spec: dict) -> None: url = reverse("headless:openapi_yaml") root = url.rpartition("/")[0] paths = spec["paths"].items() spec["paths"] = {} for path, path_spec in paths: new_path = path.replace("/_allauth", root) spec["paths"][new_path] = path_spec def pin_client(spec: dict) -> None: if len(app_settings.CLIENTS) != 1: return processed_paths = {} client_value = app_settings.CLIENTS[0] http_methods = [ "get", "post", "put", "delete", "options", "head", "patch", "trace", ] def remove_client_param(parameters: list) -> list | None: filtered = [ p for p in parameters if not ( isinstance(p, dict) and p.get("$ref") == "#/components/parameters/Client" ) ] return filtered or None for path_key, path_item in spec["paths"].items(): current_path_item = dict(path_item) processed_path_key = path_key.replace("{client}", client_value) if "parameters" in current_path_item: new_params = remove_client_param(current_path_item["parameters"]) if new_params: current_path_item["parameters"] = new_params else: current_path_item.pop("parameters") for method_name in http_methods: if method_name in current_path_item: operation_item = current_path_item[method_name] if isinstance(operation_item, dict) and "parameters" in operation_item: new_params = remove_client_param(operation_item["parameters"]) if new_params: operation_item["parameters"] = new_params else: operation_item.pop("parameters") processed_paths[processed_path_key] = current_path_item spec["paths"] = processed_paths def drop_unused_client_parameter(spec: dict) -> None: if len(app_settings.CLIENTS) != 1: return if components := spec.get("components"): if parameters := components.get("parameters"): parameters.pop("Client", None) def drop_unused_paths(spec: dict) -> set: paths = spec["paths"] used_tags = set() for path, path_spec in list(paths.items()): found_path = False for client in app_settings.CLIENTS: try: script_aware_resolve(path.replace("{client}", client)) found_path = True break except Resolver404: pass if found_path: for method, method_spec in path_spec.items(): used_tags.update(method_spec["tags"]) else: paths.pop(path) return used_tags def drop_unused_tags(spec: dict, used_tags: set) -> None: tags = spec["tags"] spec["tags"] = [] for tag in tags: if tag["name"] not in used_tags: continue spec["tags"].append(tag) def drop_unused_tag_groups(spec: dict, used_tags: set) -> None: tag_groups = spec["x-tagGroups"] spec["x-tagGroups"] = [] for tag_group in tag_groups: if any([t in used_tags for t in tag_group["tags"]]): spec["x-tagGroups"].append(tag_group) def specify_signup_fields(spec: dict) -> None: base_signup = spec["components"]["schemas"]["BaseSignup"] signup = spec["components"]["schemas"]["Signup"] properties = base_signup["properties"] required_fields = [] for field_name in ("email", "phone", "username"): field = account_settings.SIGNUP_FIELDS.get(field_name) if not field: properties.pop(field_name) elif field["required"]: required_fields.append(field_name) base_signup["required"] = required_fields password_field = account_settings.SIGNUP_FIELDS.get("password1") if not password_field: signup["allOf"] = signup["allOf"][:1] elif not password_field["required"]: signup["allOf"][1]["required"].remove("password") def specify_custom_signup_form(spec: dict) -> None: form_class = base_signup_form_class() base_signup = spec["components"]["schemas"]["BaseSignup"] for field_name, field in form_class.base_fields.items(): is_required = hasattr(field, "required") and field.required field_spec = spec_for_field(field) if is_required: base_signup["required"].append(field_name) base_signup["properties"][field_name] = field_spec def specify_user(spec: dict) -> None: dc = get_adapter().get_user_dataclass() schema, example = spec_for_dataclass(dc) spec["components"]["schemas"]["User"] = schema example_value = spec["components"]["examples"]["User"]["value"] example_value.clear() example_value.update(example) ================================================ FILE: allauth/headless/spec/urls.py ================================================ from django.urls import path from allauth.headless import app_settings from allauth.headless.spec import views urlpatterns = [ path("openapi.yaml", views.OpenAPIYAMLView.as_view(), name="openapi_yaml"), path("openapi.json", views.OpenAPIJSONView.as_view(), name="openapi_json"), ] if app_settings.SERVE_SPECIFICATION and app_settings.SPECIFICATION_TEMPLATE_NAME: urlpatterns.append( path( "openapi.html", views.OpenAPIHTMLView.as_view(), name="openapi_html", ), ) ================================================ FILE: allauth/headless/spec/views.py ================================================ import json from django.http import HttpResponse from django.utils.decorators import method_decorator from django.views.generic import TemplateView, View from allauth.account.internal.decorators import login_not_required from allauth.headless import app_settings from allauth.headless.spec.internal.schema import get_schema @method_decorator(login_not_required, name="dispatch") class OpenAPIYAMLView(View): def get(self, request) -> HttpResponse: import yaml spec = get_schema() content = yaml.dump(spec, Dumper=yaml.Dumper) return HttpResponse( content, content_type="application/vnd.oai.openapi", headers={"Content-Disposition": "inline; filename=allauth-openapi.yaml"}, ) @method_decorator(login_not_required, name="dispatch") class OpenAPIJSONView(View): def get(self, request) -> HttpResponse: spec = get_schema() content = json.dumps(spec) return HttpResponse( content, content_type="application/vnd.oai.openapi+json", headers={"Content-Disposition": "inline; filename=allauth-openapi.json"}, ) @method_decorator(login_not_required, name="dispatch") class OpenAPIHTMLView(TemplateView): def get_template_names(self) -> list[str]: return [app_settings.SPECIFICATION_TEMPLATE_NAME] ================================================ FILE: allauth/headless/templates/headless/spec/redoc_cdn.html ================================================ allauth.headless OpenAPI Specification | django-allauth ================================================ FILE: allauth/headless/templates/headless/spec/swagger_cdn.html ================================================ allauth.headless OpenAPI Specification | django-allauth
================================================ FILE: allauth/headless/tokens/__init__.py ================================================ ================================================ FILE: allauth/headless/tokens/base.py ================================================ import warnings from allauth.headless.tokens.strategies.base import AbstractTokenStrategy __all__ = ["AbstractTokenStrategy"] warnings.warn( "allauth.headless.tokens.base.AbstractTokenStrategy is deprecated, use allauth.headless.tokens.strategies.base.AbstractTokenStrategy" ) ================================================ FILE: allauth/headless/tokens/inputs.py ================================================ from allauth.headless.internal.restkit import inputs class RefreshTokenInput(inputs.Input): refresh_token = inputs.CharField() ================================================ FILE: allauth/headless/tokens/response.py ================================================ from django.http import HttpRequest from allauth.headless.base.response import APIResponse class RefreshTokenResponse(APIResponse): def __init__( self, request: HttpRequest, access_token: str, refresh_token: str | None ): data = {"access_token": access_token} if refresh_token: data["refresh_token"] = refresh_token super().__init__(request, data=data) ================================================ FILE: allauth/headless/tokens/sessions.py ================================================ import warnings from allauth.headless.tokens.strategies.sessions import SessionTokenStrategy __all__ = ["SessionTokenStrategy"] warnings.warn( "allauth.headless.tokens.sessions.SessionTokenStrategy is deprecated, use allauth.headless.tokens.strategies.sessions.SessionTokenStrategy" ) ================================================ FILE: allauth/headless/tokens/strategies/__init__.py ================================================ ================================================ FILE: allauth/headless/tokens/strategies/base.py ================================================ import abc from typing import Any from django.contrib.sessions.backends.base import SessionBase from django.http import HttpRequest class AbstractTokenStrategy(abc.ABC): def get_session_token(self, request: HttpRequest) -> str | None: """ Returns the session token, if any. """ token = request.headers.get("x-session-token") return token def create_access_token_payload( self, request: HttpRequest ) -> dict[str, Any] | None: """ After authenticating, this method is called to create the access token response payload, exposing the access token and possibly other information such as a ``refresh_token`` and ``expires_in``. """ at = self.create_access_token(request) if not at: return None return {"access_token": at} def create_access_token(self, request: HttpRequest) -> str | None: """Create an access token. While session tokens are required to handle the authentication process, depending on your requirements, a different type of token may be needed once authenticated. For example, your app likely needs access to other APIs as well. These APIs may even be implemented using different technologies, in which case having a stateless token, possibly a JWT encoding the user ID, might be a good fit. We make no assumptions in this regard. If you need access tokens, you will have to implement a token strategy that returns an access token here. """ return None @abc.abstractmethod def create_session_token(self, request: HttpRequest) -> str: """ Create a session token for the `request.session`. """ ... @abc.abstractmethod def lookup_session(self, session_token: str) -> SessionBase | None: """ Looks up the Django session given the session token. Returns `None` if the session does not / no longer exist. """ ... def refresh_token(self, refresh_token: str) -> tuple[str, str] | None: """ Validates the given refresh token, and if valid, returns a new access token and refresh token pair. """ return None ================================================ FILE: allauth/headless/tokens/strategies/jwt/__init__.py ================================================ from allauth.headless.tokens.strategies.jwt.strategy import JWTTokenStrategy __all__ = ["JWTTokenStrategy"] ================================================ FILE: allauth/headless/tokens/strategies/jwt/internal.py ================================================ import base64 import binascii import hashlib import secrets import time import uuid from dataclasses import dataclass from typing import Any from django.conf import settings from django.contrib.auth import get_user_model from django.contrib.sessions.backends.base import SessionBase from django.utils.functional import SimpleLazyObject import jwt from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from allauth.account.internal.userkit import str_to_user_id, user_id_to_str from allauth.core.internal import jwkkit from allauth.core.internal.sessionkit import get_session_user from allauth.headless import app_settings from allauth.headless.internal.sessionkit import lookup_session @dataclass class JWTConfig: signing_key: Any verifying_key: Any algorithm: str jwk_dict: dict[str, Any] | None = None def validate_access_token(token: str) -> tuple[Any, dict[str, Any]] | None: payload = decode_token(token, "access") if payload is None: return None if app_settings.JWT_STATEFUL_VALIDATION_ENABLED: session = get_token_session(payload) if session is None: return None sub = payload["sub"] pk = str_to_user_id(sub) lazy_user = SimpleLazyObject(lambda: get_user_model().objects.get(pk=pk)) return lazy_user, payload def get_session_key_cipher(initialization_vector: bytes) -> Cipher: secret_key = settings.SECRET_KEY key = hashlib.sha256(secret_key.encode()).digest() algorithm = algorithms.AES(key) mode = modes.CTR(initialization_vector) cipher = Cipher(algorithm, mode) return cipher def session_key_to_sid(session_key: str) -> str: """ In case an access token leaks, we do not want the session encoded in that token to be put to use as an X-Session-Token or session cookie. Therefore, we encrypt the sesison key. Unauthenticated symmetric encryption is fine, as the JWT is signed. """ initialization_vector = secrets.token_bytes(16) cipher = get_session_key_cipher(initialization_vector) encryptor = cipher.encryptor() encrypted_message = encryptor.update(session_key.encode()) + encryptor.finalize() sid = base64.b64encode(initialization_vector + encrypted_message).decode() return sid def session_key_from_sid(sid: str) -> str | None: try: encrypted_data = base64.b64decode(sid) except binascii.Error: return None initialization_vector = encrypted_data[:16] encrypted_message = encrypted_data[16:] if len(encrypted_message) == 0: return None cipher = get_session_key_cipher(initialization_vector) decryptor = cipher.decryptor() message_decrypted = decryptor.update(encrypted_message) + decryptor.finalize() try: return message_decrypted.decode() except UnicodeDecodeError: return None def validate_token_user(token: dict[str, Any], session: SessionBase): user = get_session_user(session) if user is None: return None sub = user_id_to_str(user) if sub != token["sub"]: return None return user def validate_refresh_token( token: str, ) -> tuple[Any, SessionBase, dict[str, Any]] | None: payload = decode_token(token, "refresh") if payload is None: return None jti = payload["jti"] session = get_token_session(payload) if session is None: return None refresh_token_jti_to_exp = get_refresh_token_state(session) exp = refresh_token_jti_to_exp.get(jti) now = time.time() if exp is None or exp <= now: return None user = validate_token_user(payload, session) return user, session, payload def get_token_session(payload: dict[str, Any]) -> SessionBase | None: sid = payload.get("sid") if not isinstance(sid, str): return None session_key = session_key_from_sid(sid) if not session_key: return None session = lookup_session(session_key) if session is None: return None return session def _get_jwt_config() -> JWTConfig: algorithm = app_settings.JWT_ALGORITHM jwk_dict = None if algorithm.startswith("HS"): key = app_settings.JWT_PRIVATE_KEY or settings.SECRET_KEY signing_key = key verifying_key = key elif algorithm.startswith("RS"): jwk_dict, signing_key = jwkkit.load_jwk_from_pem(app_settings.JWT_PRIVATE_KEY) verifying_key = signing_key.public_key() else: raise ValueError(f"Unsupported JWT algorithm: {algorithm}") return JWTConfig( signing_key=signing_key, verifying_key=verifying_key, algorithm=algorithm, jwk_dict=jwk_dict, ) def get_jwt_headers(config: JWTConfig) -> dict[str, Any]: headers = {} if config.jwk_dict: kid = config.jwk_dict.get("kid") if kid: headers["kid"] = kid return headers def decode_token(token: str, use: str) -> dict[str, Any] | None: config = _get_jwt_config() try: payload = jwt.decode( token, key=config.verifying_key, algorithms=[config.algorithm], options={ "verify_signature": True, "verify_iss": False, "verify_aud": False, "verify_exp": True, }, ) except jwt.PyJWTError: return None else: if payload.get("token_use") != use: return None for key in ("jti", "sub", "sid"): value = payload.get(key) if not isinstance(value, str): return None return payload def create_token( token_use: str, *, sub: str, sid: str, claims: dict[str, Any] | None = None, expires_in: int, ) -> tuple[str, dict[str, Any]]: config = _get_jwt_config() now = int(time.time()) payload = {} if claims is not None: payload.update(claims) payload.update( { "iat": now, "exp": now + expires_in, "sid": sid, "jti": str(uuid.uuid4()), "token_use": token_use, "sub": sub, } ) headers = get_jwt_headers(config) return ( jwt.encode( payload, config.signing_key, algorithm=config.algorithm, headers=headers, ), payload, ) def get_refresh_token_state(session: SessionBase) -> dict[str, int]: return session.setdefault("headless_refresh_tokens", {}) def create_refresh_token(user, session: SessionBase) -> str: assert user.is_authenticated # nosec assert session.session_key # nosec sub = user_id_to_str(user) sid = session_key_to_sid(session.session_key) token, payload = create_token( "refresh", sub=sub, sid=sid, expires_in=app_settings.JWT_REFRESH_TOKEN_EXPIRES_IN, ) refresh_token_jti_to_exp = get_refresh_token_state(session) refresh_token_jti_to_exp[payload["jti"]] = payload["exp"] return token def create_access_token(user, session: SessionBase, claims: dict[str, Any]) -> str: assert user.is_authenticated # nosec assert session.session_key # nosec sid = session_key_to_sid(session.session_key) sub = user_id_to_str(user) return create_token( "access", sub=sub, sid=sid, claims=claims, expires_in=app_settings.JWT_ACCESS_TOKEN_EXPIRES_IN, )[0] def invalidate_refresh_token(session: SessionBase, token: dict[str, Any]) -> None: refresh_token_jti_to_exp = get_refresh_token_state(session) jti = token["jti"] refresh_token_jti_to_exp.pop(jti, None) ================================================ FILE: allauth/headless/tokens/strategies/jwt/strategy.py ================================================ from typing import Any from django.contrib.sessions.backends.base import SessionBase from django.http import HttpRequest from allauth.core.internal.httpkit import get_authorization_credential from allauth.headless import app_settings from allauth.headless.internal import sessionkit from allauth.headless.tokens.strategies.base import AbstractTokenStrategy from allauth.headless.tokens.strategies.jwt import internal class JWTTokenStrategy(AbstractTokenStrategy): def get_session_token(self, request: HttpRequest) -> str | None: ret = super().get_session_token(request) if ret: return ret payload = self._get_access_token(request) if not payload: return None return internal.session_key_from_sid(payload["sid"]) def _get_access_token(self, request: HttpRequest): access_token = get_authorization_credential( request, app_settings.JWT_AUTHORIZATION_HEADER_SCHEME ) if access_token is None: return None user_payload = internal.validate_access_token(access_token) if user_payload is None: return None return user_payload[1] def create_session_token(self, request: HttpRequest) -> str: if not request.session.session_key: request.session.save() key = request.session.session_key # We did save assert isinstance(key, str) # nosec return key def create_access_token_payload( self, request: HttpRequest ) -> dict[str, Any] | None: ret = super().create_access_token_payload(request) if ret is not None: ret["refresh_token"] = internal.create_refresh_token( request.user, request.session ) return ret def lookup_session(self, session_token: str) -> SessionBase | None: return sessionkit.lookup_session(session_token) def create_access_token(self, request: HttpRequest) -> str | None: claims = self.get_claims(request.user) return internal.create_access_token(request.user, request.session, claims) def get_claims(self, user) -> dict[str, Any]: """ Returns additional claims to be included in the access token. Note that the following claims are reserved and will be automatically set by allauth regardless of what you return: - ``iat`` - ``exp`` - ``sid`` - ``jti`` - ``token_use`` - ``sub`` """ return {} def refresh_token(self, refresh_token: str) -> tuple[str, str] | None: user_session_payload = internal.validate_refresh_token(refresh_token) if user_session_payload is None: return None user, session, payload = user_session_payload access_token = internal.create_access_token( user, session, self.get_claims(user) ) if app_settings.JWT_ROTATE_REFRESH_TOKEN: internal.invalidate_refresh_token(session, payload) next_refresh_token = internal.create_refresh_token(user, session) else: next_refresh_token = refresh_token session.save() return access_token, next_refresh_token ================================================ FILE: allauth/headless/tokens/strategies/sessions.py ================================================ from django.contrib.sessions.backends.base import SessionBase from django.http import HttpRequest from allauth.headless.internal import sessionkit from allauth.headless.tokens.strategies.base import AbstractTokenStrategy class SessionTokenStrategy(AbstractTokenStrategy): def create_session_token(self, request: HttpRequest) -> str: if not request.session.session_key: request.session.save() key = request.session.session_key # We did save assert isinstance(key, str) # nosec return key def lookup_session(self, session_token: str) -> SessionBase | None: session_key = session_token if sessionkit.session_store().exists(session_key): return sessionkit.session_store(session_key) return None ================================================ FILE: allauth/headless/tokens/urls.py ================================================ from django.urls import path from allauth.headless.tokens import views def build_urlpatterns(client): return [ path( "tokens/refresh", views.RefreshTokenView.as_api_view(client=client), name="refresh", ), ] ================================================ FILE: allauth/headless/tokens/views.py ================================================ from django.http import HttpRequest from allauth.headless import app_settings from allauth.headless.base.views import APIView from allauth.headless.internal.restkit.response import ErrorResponse from allauth.headless.tokens.inputs import RefreshTokenInput from allauth.headless.tokens.response import RefreshTokenResponse from allauth.headless.tokens.strategies.base import AbstractTokenStrategy class RefreshTokenView(APIView): input_class = RefreshTokenInput def post(self, request: HttpRequest): refresh_token = self.input.cleaned_data["refresh_token"] strategy: AbstractTokenStrategy = app_settings.TOKEN_STRATEGY at_rt = strategy.refresh_token(refresh_token) if at_rt is None: return ErrorResponse(request) next_refresh_token: str | None access_token, next_refresh_token = at_rt if next_refresh_token == refresh_token: next_refresh_token = None return RefreshTokenResponse(request, access_token, next_refresh_token) ================================================ FILE: allauth/headless/urls.py ================================================ from django.urls import include, path from allauth import app_settings as allauth_settings from allauth.headless import app_settings from allauth.headless.account import urls as account_urls from allauth.headless.base import urls as base_urls from allauth.headless.constants import Client from allauth.headless.tokens import urls as tokens_urls def build_urlpatterns(client): patterns = [] patterns.extend(base_urls.build_urlpatterns(client)) patterns.append( path( "", include( (account_urls.build_urlpatterns(client), "headless"), namespace="account", ), ) ) if allauth_settings.SOCIALACCOUNT_ENABLED: from allauth.headless.socialaccount import urls as socialaccount_urls patterns.append( path( "", include( (socialaccount_urls.build_urlpatterns(client), "headless"), namespace="socialaccount", ), ) ) if allauth_settings.MFA_ENABLED: from allauth.headless.mfa import urls as mfa_urls patterns.append( path( "", include( (mfa_urls.build_urlpatterns(client), "headless"), namespace="mfa", ), ) ) if allauth_settings.USERSESSIONS_ENABLED: from allauth.headless.usersessions import urls as usersessions_urls patterns.append( path( "", include( (usersessions_urls.build_urlpatterns(client), "headless"), namespace="usersessions", ), ) ) if client == Client.APP: patterns.append( path( "", include( (tokens_urls.build_urlpatterns(client), "headless"), namespace="tokens", ), ) ) return [path("v1/", include(patterns))] app_name = "headless" urlpatterns = [] if Client.BROWSER in app_settings.CLIENTS: urlpatterns.append( path( "browser/", include( (build_urlpatterns(Client.BROWSER), "headless"), namespace="browser", ), ) ) if Client.APP in app_settings.CLIENTS: urlpatterns.append( path( "app/", include((build_urlpatterns(Client.APP), "headless"), namespace="app"), ) ) if app_settings.SERVE_SPECIFICATION: urlpatterns.append( path( "", include( "allauth.headless.spec.urls", ), ) ) ================================================ FILE: allauth/headless/usersessions/__init__.py ================================================ ================================================ FILE: allauth/headless/usersessions/inputs.py ================================================ from allauth.headless.internal.restkit import inputs from allauth.usersessions.models import UserSession class SelectSessionsInput(inputs.Input): sessions = inputs.ModelMultipleChoiceField(queryset=UserSession.objects.none()) def __init__(self, *args, **kwargs): self.user = kwargs.pop("user") super().__init__(*args, **kwargs) self.fields["sessions"].queryset = UserSession.objects.filter(user=self.user) ================================================ FILE: allauth/headless/usersessions/response.py ================================================ from allauth.headless.base.response import APIResponse from allauth.usersessions import app_settings class SessionsResponse(APIResponse): def __init__(self, request, sessions): super().__init__(request, data=[self._session_data(s) for s in sessions]) def _session_data(self, session) -> dict: data = { "user_agent": session.user_agent, "ip": session.ip, "created_at": session.created_at.timestamp(), "is_current": session.is_current(), "id": session.pk, } if app_settings.TRACK_ACTIVITY: data["last_seen_at"] = session.last_seen_at.timestamp() return data def get_config_data(request) -> dict: data = {"usersessions": {"track_activity": app_settings.TRACK_ACTIVITY}} return data ================================================ FILE: allauth/headless/usersessions/urls.py ================================================ from django.urls import include, path from allauth.headless.usersessions import views def build_urlpatterns(client): return [ path( "auth/", include( [ path( "sessions", views.SessionsView.as_api_view(client=client), name="sessions", ), ] ), ) ] ================================================ FILE: allauth/headless/usersessions/views.py ================================================ from django.http import HttpResponse from allauth.headless.base.response import AuthenticationResponse from allauth.headless.base.views import AuthenticatedAPIView from allauth.headless.usersessions.inputs import SelectSessionsInput from allauth.headless.usersessions.response import SessionsResponse from allauth.usersessions.internal import flows from allauth.usersessions.models import UserSession class SessionsView(AuthenticatedAPIView): input_class = {"DELETE": SelectSessionsInput} def delete(self, request, *args, **kwargs): sessions = self.input.cleaned_data["sessions"] flows.sessions.end_sessions(request, sessions) if self.request.user.is_authenticated: return self._respond_session_list() return AuthenticationResponse(request) def get(self, request, *args, **kwargs) -> HttpResponse: return self._respond_session_list() def _respond_session_list(self): sessions = UserSession.objects.purge_and_list(self.request.user) return SessionsResponse(self.request, sessions) def get_input_kwargs(self) -> dict: return {"user": self.request.user} ================================================ FILE: allauth/idp/__init__.py ================================================ ================================================ FILE: allauth/idp/oidc/__init__.py ================================================ ================================================ FILE: allauth/idp/oidc/adapter.py ================================================ import hashlib import uuid from collections.abc import Iterable from typing import Any, Literal from django.contrib.auth import get_user_model from django.core.management.utils import get_random_secret_key from django.utils.translation import gettext_lazy as _ from allauth.account.internal.userkit import ( str_to_user_id, user_id_to_str, user_username, ) from allauth.account.models import EmailAddress from allauth.core.internal.adapter import BaseAdapter from allauth.core.internal.cryptokit import generate_user_code from allauth.idp.oidc import app_settings from allauth.utils import import_attribute class DefaultOIDCAdapter(BaseAdapter): """The adapter class allows you to override various functionality of the ``allauth.idp.oidc`` app. To do so, point ``settings.IDP_OIDC_ADAPTER`` to your own class that derives from ``DefaultOIDCAdapter`` and override the behavior by altering the implementation of the methods according to your own needs. """ scope_display = { "openid": _("View your user ID"), "email": _("View your email address"), "profile": _("View your basic profile information"), } def generate_client_id(self) -> str: """ The client ID to use for newly created clients. """ return uuid.uuid4().hex def generate_client_secret(self) -> str: """ The client secret to use for newly created clients. """ return get_random_secret_key() def generate_user_code(self) -> str: return generate_user_code(**app_settings.USER_CODE_FORMAT) def hash_token(self, token: str) -> str: """ We don't store tokens directly, only the hash of the token. This methods generates that hash. """ return hashlib.sha256(token.encode("utf-8")).hexdigest() def get_issuer(self) -> str: """ Returns the URL of the issuer. """ return self.request.build_absolute_uri("/").rstrip("/") def populate_id_token( self, id_token: dict, client, scopes: Iterable[str], **kwargs ) -> None: """ This method can be used to alter the ID token payload. It is already populated with basic values. Depending on the client and requested scopes, you can expose additional information here. """ pass def populate_access_token( self, access_token: dict, *, client, scopes: Iterable[str], user, **kwargs ) -> None: """ This method can be used to alter the JWT access token payload. It is already populated with basic values. """ pass def get_claims( self, purpose: Literal["id_token", "userinfo"], user, client, scopes: Iterable[str], email: str | None = None, **kwargs, ) -> dict[str, Any]: """ Return the claims to be included in the ID token or userinfo response. """ claims = {"sub": self.get_user_sub(client, user)} if "email" in scopes: if email: try: address = EmailAddress.objects.get_for_user(user, email) except EmailAddress.DoesNotExist: pass else: address = EmailAddress.objects.get_primary(user) if address: claims.update( { "email": address.email, "email_verified": address.verified, } ) if "profile" in scopes: full_name = user.get_full_name() last_name = getattr(user, "last_name", None) first_name = getattr(user, "first_name", None) username = user_username(user) profile_claims = { "name": full_name, "given_name": first_name, "family_name": last_name, "preferred_username": username, } for claim_key, claim_value in profile_claims.items(): if claim_value: claims[claim_key] = claim_value return claims def get_user_sub(self, client, user) -> str: """ Returns the "sub" (subject identifier) for the given user. """ return user_id_to_str(user) def get_user_by_sub(self, client, sub: str): """ Looks up a user, given its subject identifier. Returns `None` if no such user was found. """ try: pk = str_to_user_id(sub) except ValueError: return None user = get_user_model().objects.filter(pk=pk).first() if not user or not user.is_active: return None return user def get_adapter() -> DefaultOIDCAdapter: return import_attribute(app_settings.ADAPTER)() ================================================ FILE: allauth/idp/oidc/admin.py ================================================ from django.contrib import admin, messages from django.utils.html import escape from django.utils.safestring import mark_safe from allauth.idp.oidc.adapter import get_adapter from allauth.idp.oidc.models import Client, Token @admin.register(Client) class ClientAdmin(admin.ModelAdmin): raw_id_fields = ("owner",) list_display = ( "name", "id", "type", "owner", "skip_consent", "allow_uri_wildcards", "created_at", ) readonly_fields = ("secret", "created_at") list_filter = ("type", "skip_consent", "allow_uri_wildcards") def save_model(self, request, obj, form, change): if not change: adapter = get_adapter() secret = adapter.generate_client_secret() obj.set_secret(secret) self.message_user( request, mark_safe( f'The client secret is only shown once: ' ), # nosec level=messages.WARNING, ) return super().save_model(request, obj, form, change) @admin.register(Token) class TokenAdmin(admin.ModelAdmin): raw_id_fields = ("client", "user") list_display = ( "client", "type", "user", "created_at", "expires_at", ) list_filter = ("type",) ================================================ FILE: allauth/idp/oidc/app_settings.py ================================================ from allauth import app_settings as allauth_settings from allauth.core.internal.cryptokit import UserCodeFormat class AppSettings: def __init__(self, prefix: str) -> None: self.prefix = prefix def _setting(self, name: str, dflt): from allauth.utils import get_setting return get_setting(f"{self.prefix}{name}", dflt) @property def ADAPTER(self) -> str: return self._setting( "ADAPTER", "allauth.idp.oidc.adapter.DefaultOIDCAdapter", ) @property def ID_TOKEN_EXPIRES_IN(self) -> int: return 5 * 60 @property def PRIVATE_KEY(self) -> str: return self._setting("PRIVATE_KEY", "") @property def ACCESS_TOKEN_EXPIRES_IN(self) -> int: return self._setting("ACCESS_TOKEN_EXPIRES_IN", 3600) @property def ACCESS_TOKEN_FORMAT(self) -> str: return self._setting("ACCESS_TOKEN_FORMAT", "opaque") @property def AUTHORIZATION_CODE_EXPIRES_IN(self) -> int: return self._setting("AUTHORIZATION_CODE_EXPIRES_IN", 60) @property def ROTATE_REFRESH_TOKEN(self) -> bool: return self._setting("ROTATE_REFRESH_TOKEN", True) @property def DEVICE_CODE_EXPIRES_IN(self) -> int: return self._setting("DEVICE_CODE_EXPIRES_IN", 300) @property def DEVICE_CODE_INTERVAL(self) -> int: return self._setting("DEVICE_CODE_INTERVAL", 5) @property def USER_CODE_FORMAT(self) -> UserCodeFormat: return self._setting("USER_CODE_FORMAT", allauth_settings.USER_CODE_FORMAT) @property def RATE_LIMITS(self) -> dict: rls = self._setting("RATE_LIMITS", {}) if rls is False: return {} ret = { # OIDC device user code checks "device_user_code": "5/m/ip" } ret.update(rls) return ret @property def RP_INITIATED_LOGOUT_ASKS_FOR_OP_LOGOUT(self) -> bool: """ At https://openid.net/specs/openid-connect-rpinitiated-1_0.html > 2. RP-Initiated Logout': > At the Logout Endpoint, the OP SHOULD ask the End-User whether to > log out of the OP as well. This setting controls whether the OP always asks. """ return self._setting("RP_INITIATED_LOGOUT_ASKS_FOR_OP_LOGOUT", True) @property def USERINFO_ENDPOINT(self) -> str | None: """ This setting can be used to point the ``userinfo_endpoint`` value as returned in the ".well-known/openid-configuration" to a custom URL. Setting this disables the built-in userinfo endpoint. """ return self._setting("USERINFO_ENDPOINT", None) _app_settings = AppSettings("IDP_OIDC_") def __getattr__(name): # See https://peps.python.org/pep-0562/ return getattr(_app_settings, name) ================================================ FILE: allauth/idp/oidc/apps.py ================================================ from django.apps import AppConfig from allauth import app_settings class OIDCConfig(AppConfig): name = "allauth.idp.oidc" label = "allauth_idp_oidc" verbose_name = "OpenID Connect IdP" default_auto_field = ( app_settings.DEFAULT_AUTO_FIELD or "django.db.models.BigAutoField" ) ================================================ FILE: allauth/idp/oidc/contrib/__init__.py ================================================ ================================================ FILE: allauth/idp/oidc/contrib/ninja/__init__.py ================================================ ================================================ FILE: allauth/idp/oidc/contrib/ninja/security.py ================================================ from django.http import HttpRequest from ninja.security.base import AuthBase from allauth.idp.oidc.internal.oauthlib.server import get_server from allauth.idp.oidc.internal.oauthlib.utils import extract_params from allauth.idp.oidc.internal.scope import is_scope_granted class TokenAuth(AuthBase): """ Use the OIDC access token to authenticate and the scopes attached to the token to authorize the request. """ openapi_type: str = "apiKey" scope = None def __init__(self, scope: str | list | dict): """The scope passed can either be: - a single scope (``str``), - a list of scopes, all of which should be granted. - a list of scope lists. Your token should match at least all scopes of one of the scope lists. - A dictionary, with the request method (e.g. ``GET``) as key, and one of the scope values from the previous bullet. The scopes to match are then dynamically selected based on the request. """ super().__init__() self.scope = scope def __call__(self, request: HttpRequest): server = get_server() orequest = extract_params(request) valid, ctx = server.verify_request(*orequest, scopes=[]) if not valid: return None if not is_scope_granted(self.scope, ctx.access_token, request.method): return None if ctx.access_token and ctx.access_token.user: request.user = ctx.access_token.user return ctx.access_token ================================================ FILE: allauth/idp/oidc/contrib/rest_framework/__init__.py ================================================ ================================================ FILE: allauth/idp/oidc/contrib/rest_framework/authentication.py ================================================ from rest_framework.authentication import BaseAuthentication from allauth.idp.oidc.internal.oauthlib.server import get_server from allauth.idp.oidc.internal.oauthlib.utils import extract_params class TokenAuthentication(BaseAuthentication): """ Use the OIDC access token to authenticate the request. """ def authenticate(self, request): server = get_server() orequest = extract_params(request) valid, ctx = server.verify_request(*orequest, scopes=[]) if not valid: return None return ctx.user, ctx.access_token ================================================ FILE: allauth/idp/oidc/contrib/rest_framework/permissions.py ================================================ from rest_framework.permissions import BasePermission from allauth.idp.oidc.internal.scope import is_scope_granted from allauth.idp.oidc.models import Token class TokenPermission(BasePermission): scope = None def has_permission(self, request, view) -> bool: access_token = request.auth if ( not isinstance(access_token, Token) or access_token.type != Token.Type.ACCESS_TOKEN ): return False return is_scope_granted(self.scope, access_token, request.method) @classmethod def has_scope(cls, scope: str | list | dict): """ Constructs and returns specific permission **class** (not instance) that checks that the request is authenticated by means of a token (see: ``TokenAuthentication``), and, that this token has the specified ``scope`` granted. The scope passed can either be: - a single scope (``str``), - a list of scopes, all of which should be granted. - a list of scope lists. Your token should match at least all scopes of one of the scope lists. - A dictionary, with the request method (e.g. ``GET``) as key, and one of the scope values from the previous bullet. The scopes to match are then dynamically selected based on the request. """ class TokenHasScopePermission(cls): # type: ignore[valid-type,misc] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.scope = scope return TokenHasScopePermission ================================================ FILE: allauth/idp/oidc/forms.py ================================================ from typing import Any from django import forms from django.forms import widgets from django.utils.translation import gettext as _ from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.models import EmailAddress from allauth.core import context from allauth.core.internal import ratelimit from allauth.idp.oidc import app_settings from allauth.idp.oidc.adapter import get_adapter from allauth.idp.oidc.internal.clientkit import clean_post_logout_redirect_uri from allauth.idp.oidc.internal.oauthlib import device_codes from allauth.idp.oidc.internal.tokens import decode_jwt_token from allauth.idp.oidc.models import Client class AuthorizationForm(forms.Form): request = forms.CharField(widget=widgets.HiddenInput) def __init__(self, *args, **kwargs) -> None: user = kwargs.pop("user") requested_scopes = kwargs.pop("requested_scopes") super().__init__(*args, **kwargs) adapter = get_adapter() choices = [(rs, adapter.scope_display.get(rs, rs)) for rs in requested_scopes] choices = sorted(choices, key=lambda ch: ch[1]) self.fields["scopes"] = forms.MultipleChoiceField( choices=choices, label=_("Grant permissions"), widget=forms.CheckboxSelectMultiple, initial=requested_scopes, required=True, ) emails = list( EmailAddress.objects.filter(user=user, verified=True) .order_by("-primary", "email") .values_list("email", flat=True) ) if "email" in requested_scopes and len(emails) > 1: self.fields["email"] = forms.ChoiceField( label=_("Email"), choices=[(email, email) for email in emails], required=False, ) class ConfirmCodeForm(forms.Form): code = forms.CharField( label=_("Code"), required=True, widget=forms.TextInput( attrs={"placeholder": _("Code"), "autocomplete": "one-time-code"}, ), ) def __init__(self, *args, **kwargs) -> None: self.code = kwargs.pop("code", None) super().__init__(*args, **kwargs) def clean_code(self) -> str: code = self.cleaned_data["code"] if not ratelimit.consume( context.request, action="device_user_code", config=app_settings.RATE_LIMITS, limit_get=True, ): raise get_account_adapter().validation_error("rate_limited") self.device_code, self.client = device_codes.validate_user_code(code) return code class DeviceAuthorizationForm(forms.Form): action = forms.CharField(required=False) class RPInitiatedLogoutForm(forms.Form): """ We don't throw validation errors in case of wrong inputs: > If any of the validation procedures defined in this specification fail, > any operations requiring the information that failed to correctly validate > MUST be aborted and the information that failed to validate MUST NOT be > used. """ action = forms.CharField(required=False, widget=forms.HiddenInput) # RECOMMENDED. ID Token previously issued by the OP to the RP passed to the # Logout Endpoint as a hint about the End-User's current authenticated # session with the Client. This is used as an indication of the identity of # the End-User that the RP is requesting be logged out by the OP. id_token_hint = forms.CharField(required=False, widget=forms.HiddenInput) # OPTIONAL. Hint to the Authorization Server about the End-User that is # logging out. The value and meaning of this parameter is left up to the # OP's discretion. For instance, the value might contain an email address, # phone number, username, or session identifier pertaining to the RP's # session with the OP for the End-User. logout_hint = forms.CharField(required=False, widget=forms.HiddenInput) # OPTIONAL. OAuth 2.0 Client Identifier valid at the Authorization # Server. When both client_id and id_token_hint are present, the OP MUST # verify that the Client Identifier matches the one used when issuing the ID # Token. The most common use case for this parameter is to specify the # Client Identifier when post_logout_redirect_uri is used but id_token_hint # is not. Another use is for symmetrically encrypted ID Tokens used as # id_token_hint values that require the Client Identifier to be specified by # other means, so that the ID Tokens can be decrypted by the OP. client_id = forms.CharField(required=False, widget=forms.HiddenInput) # OPTIONAL. URI to which the RP is requesting that the End-User's User Agent # be redirected after a logout has been performed. post_logout_redirect_uri = forms.URLField(required=False, widget=forms.HiddenInput) # OPTIONAL. Opaque value used by the RP to maintain state between the logout # request and the callback to the endpoint specified by the # post_logout_redirect_uri parameter. If included in the logout request, the # OP passes this value back to the RP using the state parameter when # redirecting the User Agent back to the RP. state = forms.CharField(required=False, widget=forms.HiddenInput) # End-User's preferred languages and scripts for the user interface, # represented as a space-separated list of BCP47 [RFC5646] language tag # values, ordered by preference. For instance, the value "fr-CA fr en" # represents a preference for French as spoken in Canada, then French # (without a region designation), followed by English (without a region # designation). An error SHOULD NOT result if some or all of the requested # locales are not supported by the OpenID Provider. ui_locales = forms.CharField(required=False, widget=forms.HiddenInput) def clean_id_token_hint(self): value = self.cleaned_data["id_token_hint"] if not value: return None payload = decode_jwt_token(value, verify_exp=False, verify_iss=True) return payload def clean(self) -> dict[str, Any] | None: cleaned_data = super().clean() if not cleaned_data: return cleaned_data post_logout_redirect_uri = cleaned_data.get("post_logout_redirect_uri") client: Client | None = None client_id = cleaned_data.get("client_id") id_token_hint = cleaned_data.get("id_token_hint") aud: str | None = None if id_token_hint: aud = id_token_hint.get("aud") if aud and client_id and aud != client_id: # aud doesn't match client_id, don't trust any of these. id_token_hint = cleaned_data["id_token_hint"] = None client_id = cleaned_data["client_id"] = None aud = None elif aud and not client_id: client_id = aud if client_id: client = Client.objects.filter(id=client_id).first() if not client: # Wipe invalid inputs. client_id = cleaned_data["client_id"] = None if aud: id_token_hint = cleaned_data["id_token_hint"] = None cleaned_data["post_logout_redirect_uri"] = clean_post_logout_redirect_uri( post_logout_redirect_uri, client ) cleaned_data["client"] = client return cleaned_data ================================================ FILE: allauth/idp/oidc/internal/__init__.py ================================================ ================================================ FILE: allauth/idp/oidc/internal/clientkit.py ================================================ import re from re import Pattern from urllib.parse import ParseResult, parse_qsl, urlparse from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ from allauth.idp.oidc.models import Client def is_loopback(parsed_uri: ParseResult) -> bool: return parsed_uri.scheme == "http" and parsed_uri.hostname in ( "127.0.0.1", "::1", ) def _validate_uri_wildcard_format(uri: str, allow_uri_wildcards: bool) -> None: if not allow_uri_wildcards: if "*" in uri: raise ValidationError( _("Wildcards are not allowed unless 'Allow URI wildcards' is enabled.") ) elif uri.count("*") > 1: raise ValidationError( _( "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is allowed." ).format(uri) ) else: try: parsed = urlparse(uri) except ValueError as e: # it's possible for this to happen with wildcards in ports raise ValidationError(_(f"Invalid URI: {e}")) if "*" in parsed.scheme or "*" in parsed.path or "*" in parsed.query: raise ValidationError( _("Wildcards are only allowed in the hostname portion of the URI.") ) def _wildcard_to_regex(wildcard: str) -> Pattern: pattern = re.escape(wildcard).replace(r"\*", r"[^.]+") return re.compile(f"^{pattern}$") def _is_scheme_hostname_allowed( parsed_uri: ParseResult, parsed_allowed_uri: ParseResult, allow_uri_wildcards: bool ) -> bool: if parsed_allowed_uri.scheme != parsed_uri.scheme: return False if ( allow_uri_wildcards and parsed_allowed_uri.hostname and "*" in parsed_allowed_uri.hostname ): allowed_hostname_pattern = _wildcard_to_regex(parsed_allowed_uri.hostname) if not allowed_hostname_pattern.match(parsed_uri.hostname): return False else: if parsed_allowed_uri.hostname != parsed_uri.hostname: return False return True def is_parsed_redirect_uri_allowed( parsed_uri: ParseResult, allowed_uri: str, allow_uri_wildcards: bool ) -> bool: parsed_allowed_uri = urlparse(allowed_uri) if not _is_scheme_hostname_allowed( parsed_uri, parsed_allowed_uri, allow_uri_wildcards ): return False if parsed_allowed_uri.path != parsed_uri.path: return False if not is_loopback(parsed_allowed_uri): if parsed_allowed_uri.port != parsed_uri.port: return False if not set(parse_qsl(parsed_allowed_uri.query)).issubset( set(parse_qsl(parsed_uri.query)) ): return False return True def is_redirect_uri_allowed( uri: str, allowed_uris: list[str], allow_uri_wildcards: bool ) -> bool: parsed_uri = urlparse(uri) return any( is_parsed_redirect_uri_allowed(parsed_uri, allowed_uri, allow_uri_wildcards) for allowed_uri in allowed_uris ) def is_origin_allowed( origin: str, allowed_origins: list[str], allow_uri_wildcards: bool ) -> bool: parsed_origin = urlparse(origin) for allowed_origin in allowed_origins: parsed_allowed_origin = urlparse(allowed_origin) if ( not _is_scheme_hostname_allowed( parsed_origin, parsed_allowed_origin, allow_uri_wildcards ) or parsed_origin.username != parsed_allowed_origin.username or parsed_origin.password != parsed_allowed_origin.password or parsed_origin.port != parsed_allowed_origin.port ): continue else: return True return False def get_used_schemes(client: Client) -> set[str]: schemes = set() for uri in client.get_redirect_uris(): parsed = urlparse(uri) if parsed.scheme: schemes.add(parsed.scheme) return schemes def clean_post_logout_redirect_uri( post_logout_redirect_uri: str | None, client: Client | None ) -> str | None: """ This URI SHOULD use the https scheme and MAY contain port, path, and query parameter components; however, it MAY use the http scheme, provided that the Client Type is confidential, as defined in Section 2.1 of OAuth 2.0 [RFC6749], and provided the OP allows the use of http RP URIs. The URI MAY use an alternate scheme, such as one that is intended to identify a callback into a native application. The value MUST have been previously registered with the OP, either using the post_logout_redirect_uris Registration parameter or via another mechanism. An id_token_hint is also RECOMMENDED when this parameter is included. """ allowed_schemes = {"https"} if client: allowed_schemes.update(get_used_schemes(client)) if client.type == Client.Type.CONFIDENTIAL: allowed_schemes.add("http") parsed = urlparse(post_logout_redirect_uri) if not parsed.scheme or parsed.scheme not in allowed_schemes: return None return post_logout_redirect_uri ================================================ FILE: allauth/idp/oidc/internal/flows.py ================================================ from django.http import HttpRequest from allauth.account.internal.flows.logout import logout from allauth.idp.oidc.models import Client, Token def rp_initiated_logout( request: HttpRequest, *, from_op: bool, post_logout_redirect_uri: str | None = None, client: Client | None = None, ): if not request.user.is_authenticated: return if client: Token.objects.filter( user=request.user, client=client, type__in=[Token.Type.ACCESS_TOKEN, Token.Type.REFRESH_TOKEN], ).delete() if from_op: has_redirect_uri = bool(post_logout_redirect_uri) logout(request, show_message=not has_redirect_uri) ================================================ FILE: allauth/idp/oidc/internal/oauthlib/__init__.py ================================================ ================================================ FILE: allauth/idp/oidc/internal/oauthlib/authorization_codes.py ================================================ from django.core.cache import cache from allauth.account.models import EmailAddress from allauth.idp.oidc import app_settings from allauth.idp.oidc.adapter import get_adapter from allauth.idp.oidc.models import Client def cache_key(client_id: str, code: str) -> str: return f"allauth.idp.oidc.authorization_code[{client_id}:{code}]" def create(client: Client, code: dict, request) -> None: adapter = get_adapter() authorization_code = { "code": code, "client_id": client.id, "redirect_uri": request.redirect_uri, "sub": adapter.get_user_sub(client, request.user), "scopes": request.scopes, "claims": request.claims, } if email := getattr(request, "email", None): # Don't trouble ourselves with keeping track a specific email in case # the primary was chosen. if EmailAddress.objects.get_primary_email(request.user) != email.lower(): authorization_code["email"] = email code_challenge = getattr(request, "code_challenge", None) if code_challenge: authorization_code["pkce"] = { "code_challenge": code_challenge, "code_challenge_method": request.code_challenge_method, } cache.set( cache_key(client.id, code["code"]), authorization_code, timeout=app_settings.AUTHORIZATION_CODE_EXPIRES_IN, ) def lookup(client_id: str, code: str) -> dict | None: return cache.get(cache_key(client_id, code)) def invalidate(client_id: str, code: str) -> None: cache.delete(cache_key(client_id, code)) def validate(client_id: str, code: str, request) -> bool: authorization_code = lookup(client_id, code) if not authorization_code: return False user = get_adapter().get_user_by_sub(request.client, authorization_code["sub"]) if not user: return False request.scopes = authorization_code["scopes"] request.user = user pkce = authorization_code.get("pkce") if pkce: request.code_challenge = pkce["code_challenge"] request.code_challenge_method = pkce["code_challenge_method"] request.claims = authorization_code["claims"] request.email = authorization_code.get("email") return True ================================================ FILE: allauth/idp/oidc/internal/oauthlib/device_codes.py ================================================ import time from django.contrib.auth import get_user_model from django.contrib.auth.models import AbstractBaseUser from django.core.cache import cache from django.http import HttpRequest from oauthlib.oauth2.rfc6749.errors import ( InvalidClientError, InvalidGrantError, InvalidRequestError, UnsupportedGrantTypeError, ) from oauthlib.oauth2.rfc8628.errors import ( AccessDenied, AuthorizationPendingError, SlowDownError, ) from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.internal.userkit import str_to_user_id, user_id_to_str from allauth.core.internal.cryptokit import compare_user_code from allauth.idp.oidc.models import Client def cache_user_code_key(user_code: str): return f"allauth.idp.oidc.user_code[{user_code.lower()}]" def cache_device_code_key(device_code: str): return f"allauth.idp.oidc.device_code[{device_code}]" def create(client_id: str, scope: list[str] | None, data: dict): cache.set( cache_user_code_key(data["user_code"]), data["device_code"], timeout=data["expires_in"], ) cache.set( cache_device_code_key(data["device_code"]), { "expires_at": time.time() + data["expires_in"], "granted": None, "last_poll_at": 0, "client_id": client_id, "scope": scope, "device": data, }, timeout=data["expires_in"], ) def lookup_client(client_id: str) -> Client | None: client = Client.objects.filter(id=client_id).first() if not client: return None if client.type != Client.Type.PUBLIC: return None return client def validate_user_code(code: str) -> tuple[str, Client]: data: dict | None = None device_code = cache.get(cache_user_code_key(code)) if device_code: data = cache.get(cache_device_code_key(device_code)) if ( not data or data["granted"] is not None or not compare_user_code(actual=code, expected=data["device"]["user_code"]) ): raise get_account_adapter().validation_error("incorrect_code") client = lookup_client(data["client_id"]) if not client: raise get_account_adapter().validation_error("incorrect_code") return device_code, client def confirm_or_deny_device_code(user, device_code: str, confirm: bool) -> bool: data = cache.get(cache_device_code_key(device_code)) if data is None or data["granted"] is not None: return False data["granted"] = confirm data["user"] = user_id_to_str(user) return update_device_state(device_code, data) def update_device_state(device_code: str, data: dict) -> bool: timeout = int(data["expires_at"] - time.time()) if timeout < 0: return False cache.set(cache_device_code_key(device_code), data, timeout=timeout) return True def poll_device_code( request: HttpRequest, ) -> tuple[AbstractBaseUser, dict]: client_id = request.POST.get("client_id") device_code = request.POST.get("device_code") if not client_id or not device_code: raise InvalidRequestError client = lookup_client(client_id) if not client: raise InvalidClientError if client.GrantType.DEVICE_CODE not in client.get_grant_types(): raise UnsupportedGrantTypeError cache_key = cache_device_code_key(device_code) data = cache.get(cache_key) if data is None or data["client_id"] != client_id: raise InvalidGrantError now = time.time() if data["last_poll_at"] + data["device"]["interval"] > now: raise SlowDownError data["last_poll_at"] = now update_device_state(device_code, data) granted = data.get("granted") if granted is None: raise AuthorizationPendingError elif granted is False: raise AccessDenied assert granted is True # nosec cache.delete(cache_key) user = get_user_model().objects.filter(pk=str_to_user_id(data["user"])).first() if user is None or not user.is_active: raise AccessDenied return user, data ================================================ FILE: allauth/idp/oidc/internal/oauthlib/request_validator.py ================================================ import uuid from datetime import timedelta from django.utils import timezone import jwt from oauthlib.openid import RequestValidator from allauth.core import context from allauth.core.internal import jwkkit from allauth.idp.oidc import app_settings from allauth.idp.oidc.adapter import get_adapter from allauth.idp.oidc.internal.clientkit import ( is_origin_allowed, is_redirect_uri_allowed, ) from allauth.idp.oidc.internal.oauthlib import authorization_codes from allauth.idp.oidc.internal.tokens import decode_jwt_token from allauth.idp.oidc.models import Client, Token class OAuthLibRequestValidator(RequestValidator): def validate_client_id(self, client_id: str, request): client = self._lookup_client(request, client_id) if not client: return False self._use_client(request, client) return True def validate_redirect_uri(self, client_id, redirect_uri, request, *args, **kwargs): return is_redirect_uri_allowed( redirect_uri, request.client.get_redirect_uris(), request.client.allow_uri_wildcards, ) def validate_response_type( self, client_id, response_type, client, request, *args, **kwargs ): return response_type in request.client.get_response_types() def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs): return set(scopes).issubset(request.client.get_scopes()) def get_default_scopes(self, client_id, request, *args, **kwargs): return request.client.get_default_scopes() def save_authorization_code(self, client_id, code, request, *args, **kwargs): # WORKAROUND: docstring says: # > To support OIDC, you MUST associate the code with: # > - nonce, if present (``code["nonce"]``) # Yet, nonce is not there, it is in request.nonce. nonce = getattr(request, "nonce", None) if nonce: code = dict(**code, nonce=nonce) # (end WORKAROUND) authorization_codes.create(request.client, code, request) def authenticate_client_id(self, client_id, request, *args, **kwargs) -> bool: """Ensure client_id belong to a non-confidential client.""" client = self._lookup_client(request, client_id) if not client or client.type != Client.Type.PUBLIC: return False self._use_client(request, client) return True def authenticate_client(self, request, *args, **kwargs) -> bool: client_id = getattr(request, "client_id", None) client_secret = getattr(request, "client_secret", None) if not isinstance(client_id, str): return False if not client_secret and request.grant_type == Client.GrantType.DEVICE_CODE: return self.authenticate_client_id(client_id, request) if not client_secret or not isinstance(client_secret, str): return False client = self._lookup_client(request, client_id) if not client: return False if not client.check_secret(client_secret): return False self._use_client(request, client) return True def validate_grant_type( self, client_id, grant_type, client, request, *args, **kwargs ): return grant_type in client.get_grant_types() def validate_code(self, client_id, code, client, request, *args, **kwargs): return authorization_codes.validate(client_id, code, request) def confirm_redirect_uri( self, client_id, code, redirect_uri, client, request, *args, **kwargs ) -> bool: authorization_code = self._lookup_authorization_code(request, client_id, code) if not authorization_code: return False return redirect_uri == authorization_code["redirect_uri"] def save_bearer_token(self, token: dict, request, *args, **kwargs): """ https://datatracker.ietf.org/doc/html/rfc6749#section-6 > The authorization server MAY issue a new refresh token, in which case > the client MUST discard the old refresh token and replace it with the > new refresh token. The authorization server MAY revoke the old > refresh token after issuing a new refresh token to the client. If a > new refresh token is issued, the refresh token scope MUST be > identical to that of the refresh token included by the client in the > request. https://datatracker.ietf.org/doc/html/rfc6749#section-1.5 > Refresh tokens are issued to the client by the authorization server and > are used to obtain a new access token when the current access token becomes > invalid or expires, or to obtain additional access tokens with identical or > narrower scope """ adapter = get_adapter() refresh_token = token.get("refresh_token") email = getattr(request, "email", None) tokens = [] if refresh_token: refresh_token_hash = adapter.hash_token(refresh_token) rt = getattr(request, "refresh_token_instance", None) if rt and not email and "email" in request.scopes: email = rt.get_scope_email() if ( rt and not app_settings.ROTATE_REFRESH_TOKEN and refresh_token_hash == rt.hash ): # We reuse our token. pass else: if rt: # If we have an existing refresh token, drop it, because of: assert ( app_settings.ROTATE_REFRESH_TOKEN or refresh_token_hash != rt.hash ) # nosec[assert_used] rt.delete() tokens.append( Token( client=request.client, user=request.user, type=Token.Type.REFRESH_TOKEN, hash=refresh_token_hash, ) ) tokens.append( Token( client=request.client, user=request.user, type=Token.Type.ACCESS_TOKEN, hash=adapter.hash_token(token["access_token"]), expires_at=timezone.now() + timedelta(seconds=token["expires_in"]), ) ) for t in tokens: t.set_scopes(request.scopes) if email: t.set_scope_email(email) Token.objects.bulk_create(tokens) def invalidate_authorization_code(self, client_id, code, request, *args, **kwargs): authorization_codes.invalidate(client_id, code) def validate_user_match(self, id_token_hint, scopes, claims, request) -> bool: if not context.request.user: return False sub = None if id_token_hint: payload = decode_jwt_token( id_token_hint, client_id=request.client.id, verify_exp=True, verify_iss=True, ) if payload is None: return False sub = payload.get("sub") session_sub = get_adapter().get_user_sub( request.client, context.request.user ) if sub != session_sub: return False if claims: sub = claims.get("sub") session_sub = get_adapter().get_user_sub( request.client, context.request.user ) if sub != session_sub: return False return True def get_authorization_code_scopes( self, client_id, code, redirect_uri, request ) -> list[str]: authorization_code = self._lookup_authorization_code(request, client_id, code) if not authorization_code: return [] return authorization_code["scopes"] def get_authorization_code_nonce(self, client_id, code, redirect_uri, request): authorization_code = self._lookup_authorization_code(request, client_id, code) return authorization_code["code"].get("nonce") def get_code_challenge(self, code, request): ret = None authorization_code = self._lookup_authorization_code( request, request.client_id, code ) if pkce := authorization_code.get("pkce"): ret = pkce["code_challenge"] return ret def get_code_challenge_method(self, code, request): ret = None authorization_code = self._lookup_authorization_code( request, request.client_id, code ) if pkce := authorization_code.get("pkce"): ret = pkce["code_challenge_method"] return ret def is_pkce_required(self, client_id, request) -> bool: client = self._lookup_client(request, client_id) return bool(client and client.type == Client.Type.PUBLIC) def finalize_id_token(self, id_token: dict, token: dict, token_handler, request): """ https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims """ adapter = get_adapter() id_token["iss"] = adapter.get_issuer() id_token["exp"] = id_token["iat"] + app_settings.ID_TOKEN_EXPIRES_IN id_token["jti"] = uuid.uuid4().hex email = getattr(request, "email", None) id_token.update( adapter.get_claims( "id_token", request.user, request.client, request.scopes, email=email ) ) adapter.populate_id_token(id_token, request.client, request.scopes) jwk_dict, private_key = jwkkit.load_jwk_from_pem(app_settings.PRIVATE_KEY) return jwt.encode( id_token, private_key, algorithm="RS256", headers={"kid": jwk_dict["kid"]} ) def validate_bearer_token(self, token, scopes, request) -> bool: if not token: return False if context.request.GET.get("access_token") == token: # Supporting tokens in query params is considered bad practice, yet, # oauthlib supports this. E.g., if access tokens are sent via URI # query parameters, such tokens may leak to log files and the HTTP # 'referer'. return False instance = Token.objects.lookup(Token.Type.ACCESS_TOKEN, token) if not instance: return False if instance.user and not instance.user.is_active: return False granted_scopes = instance.get_scopes() if not set(scopes).issubset(set(granted_scopes)): return False request.user = instance.user self._use_client(request, instance.client) request.scopes = granted_scopes request.access_token = instance return True def revoke_token(self, token, token_type_hint, request, *args, **kwargs): if token_type_hint == "access_token": # nosec types = [Token.Type.ACCESS_TOKEN] elif token_type_hint == "refresh_token": # nosec types = [Token.Type.REFRESH_TOKEN] else: types = [Token.Type.ACCESS_TOKEN, Token.Type.REFRESH_TOKEN] Token.objects.by_value(token).filter(type__in=types).delete() def get_userinfo_claims(self, request): email = request.access_token.get_scope_email() return get_adapter().get_claims( "userinfo", request.user, request.client, request.scopes, email=email ) def get_default_redirect_uri(self, client_id, request, *args, **kwargs): # https://openid.net/specs/openid-financial-api-part-1-1_0.html#section-5.2.2 # 9. shall require the redirect_uri in the authorization request; # So, don't support a default. return None def validate_user(self, username, password, client, request, *args, **kwargs): """ Note that this bypasses MFA, which is why the password grant is not recommended and hence disabled. This could work: try: user = get_account_adapter().authenticate( context.request, username=username, password=password ) except ValidationError: return False else: if not user: return False request.user = user return True """ return False def validate_refresh_token(self, refresh_token, client, request, *args, **kwargs): token = Token.objects.filter(client=client).lookup( Token.Type.REFRESH_TOKEN, refresh_token ) if not token: return False if not token.user or not token.user.is_active: return False request.user = token.user request.refresh_token_instance = token return True def get_original_scopes(self, refresh_token, request, *args, **kwargs): return request.refresh_token_instance.get_scopes() def client_authentication_required(self, request, *args, **kwargs) -> bool: if request.client_id and request.client_secret: return True client = self._lookup_client(request, request.client_id) if client and client.type == Client.Type.PUBLIC: return False return super().client_authentication_required(request, *args, **kwargs) def _lookup_client(self, request, client_id) -> Client | None: """ In various places, oauthlib documents: Note, while not strictly necessary it can often be very convenient to set request.client to the client object associated with the given client_id. It's unclear though that if this is not explicitly stated, and, we still were to set request.client, whether that could have adverse side effects. So, don't assign request.client here. """ cache = request._client_cache = getattr(request, "_client_cache", {}) if client_id in cache: client = cache[client_id] else: client = Client.objects.filter(id=client_id).first() cache[client_id] = client return client def _use_client(self, request, client: Client) -> None: request.client = client request.client.client_id = client.id def _lookup_authorization_code( self, request, client_id: str, code: str ) -> dict | None: cache = request._code_cache = getattr(request, "_code_cache", {}) key = (client_id, code) if key in cache: authorization_code = cache[key] else: authorization_code = authorization_codes.lookup(client_id, code) cache[key] = authorization_code return authorization_code def is_origin_allowed(self, client_id, origin, request, *args, **kwargs) -> bool: client = self._lookup_client(request, client_id) return bool( client and is_origin_allowed( origin, client.get_cors_origins(), client.allow_uri_wildcards ) ) def rotate_refresh_token(self, request): return app_settings.ROTATE_REFRESH_TOKEN def validate_silent_login(self, request) -> bool: if context.request.user.is_authenticated: request.user = context.request.user return True return False def validate_silent_authorization(self, request) -> bool: granted_scopes = set() tokens = Token.objects.valid().filter( user=context.request.user, type__in=[Token.Type.REFRESH_TOKEN, Token.Type.ACCESS_TOKEN], ) for token in tokens.iterator(): granted_scopes.update(token.get_scopes()) return set(request.scopes).issubset(granted_scopes) def validate_jwt_bearer_token(self, token, scopes, request): if scopes: # We don't have scope for the ID token return False payload = decode_jwt_token(token, verify_iss=True, verify_exp=True) if payload is None: return False return self.validate_client_id(payload["aud"], request) ================================================ FILE: allauth/idp/oidc/internal/oauthlib/server.py ================================================ import secrets import time import uuid from django.urls import reverse import jwt from oauthlib.oauth2.rfc8628.endpoints import DeviceApplicationServer from oauthlib.openid import Server from allauth.core import context from allauth.core.internal import jwkkit from allauth.idp.oidc import app_settings from allauth.idp.oidc.adapter import get_adapter from allauth.idp.oidc.internal.oauthlib.request_validator import ( OAuthLibRequestValidator, ) def generate_opaque_token(request): # 160 bit token is recommended, oauthlib uses less. # oauch.io -- at oautlib's default, we get: # Out of 11 valid authorization responses, the # average calculated entropy for the access tokens was 144,3 (±7,1) bits return secrets.token_urlsafe(64) def generate_jwt_access_token(request) -> str: adapter = get_adapter() iat = int(time.time()) access_token = { "client_id": request.client.id, "iss": adapter.get_issuer(), "iat": iat, "exp": iat + app_settings.ACCESS_TOKEN_EXPIRES_IN, "jti": uuid.uuid4().hex, "token_use": "access", # nosec } # Client credentials has no user. if request.user is not None: access_token["sub"] = adapter.get_user_sub(request.client, request.user) if request.scopes: access_token["scope"] = " ".join(request.scopes) adapter.populate_access_token( access_token, user=request.user, client=request.client, scopes=request.scopes ) jwk_dict, private_key = jwkkit.load_jwk_from_pem(app_settings.PRIVATE_KEY) return jwt.encode( access_token, private_key, algorithm="RS256", headers={"kid": jwk_dict["kid"]} ) def generate_access_token(request) -> str: fmt = app_settings.ACCESS_TOKEN_FORMAT if fmt == "opaque": return generate_opaque_token(request) elif fmt == "jwt": return generate_jwt_access_token(request) else: raise ValueError(fmt) def generate_refresh_token(request) -> str: return generate_opaque_token(request) class OAuthLibServer(Server): def __init__(self, **kwargs): super().__init__( token_generator=generate_access_token, refresh_token_generator=generate_refresh_token, request_validator=OAuthLibRequestValidator(), token_expires_in=app_settings.ACCESS_TOKEN_EXPIRES_IN, **kwargs, ) class DeviceOAuthLibServer(DeviceApplicationServer): def __init__(self): verification_uri = context.request.build_absolute_uri( reverse("idp:oidc:device_authorization") ) super().__init__( request_validator=OAuthLibRequestValidator(), verification_uri=verification_uri, verification_uri_complete=f"{verification_uri}?code={{user_code}}", interval=app_settings.DEVICE_CODE_INTERVAL, user_code_generator=lambda: get_adapter().generate_user_code(), ) self._expires_in = app_settings.DEVICE_CODE_EXPIRES_IN def get_server(**kwargs): return OAuthLibServer(**kwargs) def get_device_server(): return DeviceOAuthLibServer() ================================================ FILE: allauth/idp/oidc/internal/oauthlib/utils.py ================================================ from urllib.parse import urlparse, urlunparse from django.forms import Form from django.http import HttpRequest, HttpResponse, JsonResponse from django.shortcuts import render from oauthlib.common import quote, urlencode, urlencoded from oauthlib.oauth2.rfc6749.errors import OAuth2Error from allauth.account import app_settings as account_settings def get_uri(request: HttpRequest) -> str: """ Django considers "safe" some characters that aren't so for oauthlib. We have to search for them and properly escape. """ parsed = list(urlparse(request.get_full_path())) query = parsed[4] encoded_query = quote(query, safe="".join(urlencoded)) parsed[4] = encoded_query return urlunparse(parsed) def extract_params(request: HttpRequest) -> tuple[str, str, str, dict[str, str]]: uri = get_uri(request) body: str = urlencode(request.POST.items()) headers = extract_headers(request) if request.method is None: raise ValueError(request.method) return uri, request.method, body, headers def extract_headers(request) -> dict[str, str]: """ You need to define extract_params and make sure it does not include file like objects waiting for input. In Django this is request.META['wsgi.input'] and request.META['wsgi.errors'] """ headers = request.META.copy() headers.pop("wsgi.input", None) headers.pop("wsgi.errors", None) if "HTTP_AUTHORIZATION" in headers: headers["Authorization"] = headers["HTTP_AUTHORIZATION"] if "HTTP_ORIGIN" in headers: headers["Origin"] = headers["HTTP_ORIGIN"] if "CONTENT_TYPE" in headers: headers["Content-Type"] = headers["CONTENT_TYPE"] return headers def convert_response(headers, body, status): if isinstance(body, dict): response = JsonResponse(body, status=status) else: response = HttpResponse(content=body, status=status) for k, v in headers.items(): response[k] = v return response def respond_html_error( request: HttpRequest, *, error: OAuth2Error | None = None, form: Form | None = None, ) -> HttpResponse: context = {"error": error, "error_form": form} return render( request, f"idp/oidc/error.{account_settings.TEMPLATE_EXTENSION}", context, ) def respond_json_error(request: HttpRequest, error: OAuth2Error) -> HttpResponse: response = HttpResponse( error.json, status=error.status_code, content_type="application/json" ) for k, v in error.headers.items(): response[k] = v return response ================================================ FILE: allauth/idp/oidc/internal/scope.py ================================================ from allauth.idp.oidc.models import Token def _is_scope_granted( scope: str | list[str] | list[list[str]], granted_scope: list[str], ) -> bool: if isinstance(scope, str): return scope in granted_scope if not isinstance(scope, list): raise ValueError if len(scope) == 0: return True list_of_list_of_scopes: list[list[str]] if isinstance(scope[0], str): list_of_list_of_scopes = [scope] # type: ignore else: list_of_list_of_scopes = scope # type: ignore for list_of_scopes in list_of_list_of_scopes: if all(s in granted_scope for s in list_of_scopes): return True return False def is_scope_granted( scope: ( None | str | list[str] | list[list[str]] | dict[str, str | list[str] | list[list[str]]] ), token: Token, method: str | None = None, ) -> bool: if scope is None: return True if isinstance(scope, dict): if not method: return False scope = scope.get(method) granted_scope = token.get_scopes() if token else [] assert scope is not None # nosec return _is_scope_granted(scope, granted_scope) ================================================ FILE: allauth/idp/oidc/internal/tokens.py ================================================ import jwt from allauth.core.internal import jwkkit from allauth.idp.oidc import app_settings from allauth.idp.oidc.adapter import get_adapter def decode_jwt_token( value: str, *, client_id: str | None = None, verify_exp: bool, verify_iss: bool ) -> dict | None: if not value: return None try: jwk_dict, private_key = jwkkit.load_jwk_from_pem(app_settings.PRIVATE_KEY) issuer: str | None = None audience: str | None = None if client_id: audience = client_id if verify_iss: issuer = get_adapter().get_issuer() return jwt.decode( value, key=private_key.public_key(), algorithms=["RS256"], options={ "verify_signature": True, "verify_iss": verify_iss, "verify_aud": client_id is not None, "verify_exp": verify_exp, }, audience=audience, issuer=issuer, ) except jwt.PyJWTError: return None ================================================ FILE: allauth/idp/oidc/migrations/0001_initial.py ================================================ import django.db.models.deletion import django.utils.timezone from django.conf import settings from django.db import migrations, models import allauth.idp.oidc.models class Migration(migrations.Migration): initial = True dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name="Client", fields=[ ( "id", models.CharField( default=allauth.idp.oidc.models.default_client_id, max_length=100, primary_key=True, serialize=False, verbose_name="Client ID", ), ), ("name", models.CharField(max_length=100)), ( "secret", models.CharField( default=allauth.idp.oidc.models.default_client_secret, max_length=200, ), ), ( "scopes", models.TextField( default="openid", help_text="The scope(s) the client is allowed to request. Provide one value per line, e.g.: openid(ENTER)profile(ENTER)email(ENTER)", ), ), ( "type", models.CharField( choices=[ ("confidential", "Confidential"), ("public", "Public"), ], default="confidential", max_length=20, ), ), ( "grant_types", models.TextField( default="authorization_code", help_text="A list of allowed grant types. Provide one value per line, e.g.: authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)", ), ), ( "redirect_uris", models.TextField( blank=True, default="", help_text="A list of allowed redirect (callback) URLs, one per line.", ), ), ( "cors_origins", models.TextField( blank=True, default="", help_text="A list of allowed origins for cross-origin requests, one per line.", verbose_name="CORS allowed origins", ), ), ( "response_types", models.TextField( default="code", help_text="A list of allowed response types. Provide one value per line, e.g.: code(ENTER)id_token token(ENTER)", ), ), ( "skip_consent", models.BooleanField( default=False, help_text="Flag to allow skip the consent screen for this client", ), ), ("created_at", models.DateTimeField(default=django.utils.timezone.now)), ("data", models.JSONField(blank=True, default=None, null=True)), ( "owner", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, ), ), ], options={ "verbose_name": "client", "verbose_name_plural": "clients", }, ), migrations.CreateModel( name="Token", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ( "type", models.CharField( choices=[ ("ia", "Initial access token"), ("at", "Access token"), ("rt", "Refresh token"), ("ac", "Authorization code"), ], max_length=2, ), ), ("hash", models.CharField(max_length=255)), ("data", models.JSONField(blank=True, default=None, null=True)), ("created_at", models.DateTimeField(default=django.utils.timezone.now)), ( "expires_at", models.DateTimeField(blank=True, db_index=True, null=True), ), ("scopes", models.TextField(default="")), ( "client", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="allauth_idp_oidc.client", ), ), ( "user", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, ), ), ], options={ "unique_together": {("type", "hash")}, }, ), ] ================================================ FILE: allauth/idp/oidc/migrations/0002_client_default_scopes.py ================================================ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("allauth_idp_oidc", "0001_initial"), ] operations = [ migrations.AddField( model_name="client", name="default_scopes", field=models.TextField( blank=True, default="", help_text="In case the client does not specify any scope, these default scopes are used. Provide one value per line, e.g.: openid(ENTER)profile(ENTER)email(ENTER)", ), ), ] ================================================ FILE: allauth/idp/oidc/migrations/0003_client_allow_uri_wildcards.py ================================================ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("allauth_idp_oidc", "0002_client_default_scopes"), ] operations = [ migrations.AddField( model_name="client", name="allow_uri_wildcards", field=models.BooleanField( default=False, help_text="Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs can contain a single asterisk to match subdomains.", verbose_name="Allow URI wildcards", ), ), ] ================================================ FILE: allauth/idp/oidc/migrations/__init__.py ================================================ ================================================ FILE: allauth/idp/oidc/models.py ================================================ from django.conf import settings from django.contrib.auth.hashers import check_password, make_password from django.db import models from django.db.models import Q from django.utils import timezone from django.utils.translation import gettext_lazy as _ from allauth.idp.oidc.adapter import get_adapter def default_client_id() -> str: adapter = get_adapter() client_id = adapter.generate_client_id() return client_id def default_client_secret() -> str: adapter = get_adapter() client_secret = adapter.generate_client_secret() return make_password(client_secret) def _values_from_text(text) -> list[str]: return list(filter(None, [s.strip() for s in text.split("\n")])) def _values_to_text(values) -> str: if isinstance(values, str): raise ValueError(values) return "\n".join(values) class Client(models.Model): class GrantType(models.TextChoices): AUTHORIZATION_CODE = "authorization_code", _("Authorization code") DEVICE_CODE = "urn:ietf:params:oauth:grant-type:device_code", _("Device code") CLIENT_CREDENTIALS = "client_credentials", _("Client credentials") REFRESH_TOKEN = "refresh_token", _("Refresh token") class Type(models.TextChoices): CONFIDENTIAL = "confidential", _("Confidential") PUBLIC = "public", _("Public") id = models.CharField( primary_key=True, max_length=100, default=default_client_id, verbose_name="Client ID", ) name = models.CharField( max_length=100, ) secret = models.CharField(max_length=200, default=default_client_secret) scopes = models.TextField( help_text=_( "The scope(s) the client is allowed to request. Provide one value per line, e.g.: openid(ENTER)profile(ENTER)email(ENTER)" ), default="openid", ) default_scopes = models.TextField( help_text=_( "In case the client does not specify any scope, these default scopes are used. Provide one value per line, e.g.: openid(ENTER)profile(ENTER)email(ENTER)" ), default="", blank=True, ) type = models.CharField( max_length=20, default=Type.CONFIDENTIAL, choices=Type.choices ) grant_types = models.TextField( default=GrantType.AUTHORIZATION_CODE, help_text=_( "A list of allowed grant types. Provide one value per line, e.g.: authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" ), ) redirect_uris = models.TextField( help_text="A list of allowed redirect (callback) URLs, one per line.", blank=True, default="", ) cors_origins = models.TextField( blank=True, help_text=_( "A list of allowed origins for cross-origin requests, one per line." ), default="", verbose_name="CORS allowed origins", ) allow_uri_wildcards = models.BooleanField( default=False, help_text=_( "Allow wildcards (*) in redirect URIs and CORS origins. " "When enabled, URIs can contain a single asterisk to match subdomains." ), verbose_name="Allow URI wildcards", ) response_types = models.TextField( default="code", help_text=_( "A list of allowed response types. Provide one value per line, e.g.: code(ENTER)id_token token(ENTER)" ), ) owner = models.ForeignKey( settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.CASCADE ) skip_consent = models.BooleanField( default=False, help_text="Flag to allow skip the consent screen for this client" ) created_at = models.DateTimeField(default=timezone.now) data = models.JSONField(blank=True, null=True, default=None) class Meta: verbose_name = _("client") verbose_name_plural = _("clients") def get_redirect_uris(self) -> list[str]: return _values_from_text(self.redirect_uris) def set_redirect_uris(self, uris: list[str]) -> None: self.redirect_uris = _values_to_text(uris) def get_cors_origins(self) -> list[str]: return _values_from_text(self.cors_origins) def set_cors_origins(self, uris: list[str]) -> None: self.cors_origins = _values_to_text(uris) def get_scopes(self) -> list[str]: return _values_from_text(self.scopes) def set_scopes(self, scopes: list[str]) -> None: self.scopes = _values_to_text(scopes) def get_default_scopes(self) -> list[str]: return _values_from_text(self.default_scopes) def set_default_scopes(self, scopes: list[str]) -> None: self.default_scopes = _values_to_text(scopes) def get_response_types(self) -> list[str]: return _values_from_text(self.response_types) def set_response_types(self, response_types: list[str]) -> None: self.response_types = _values_to_text(response_types) def get_grant_types(self) -> list[str]: return _values_from_text(self.grant_types) def set_grant_types(self, grant_types: list[str]) -> None: self.grant_types = _values_to_text(grant_types) def set_secret(self, secret) -> None: self.secret = make_password(secret) def check_secret(self, secret: str) -> bool: return check_password(secret, self.secret) def clean_redirect_uris(self) -> list[str]: from allauth.idp.oidc.internal.clientkit import _validate_uri_wildcard_format uris = self.get_redirect_uris() for uri in uris: _validate_uri_wildcard_format(uri, self.allow_uri_wildcards) return uris def clean_cors_origins(self) -> list[str]: from allauth.idp.oidc.internal.clientkit import _validate_uri_wildcard_format origins = self.get_cors_origins() for origin in origins: _validate_uri_wildcard_format(origin, self.allow_uri_wildcards) return origins def clean(self) -> None: # the django admin doesn't call full_clean, so we need to call them here self.clean_redirect_uris() self.clean_cors_origins() def __str__(self) -> str: return self.id class TokenQuerySet(models.query.QuerySet): def valid(self): return self.filter( Q(expires_at__isnull=True) | Q(expires_at__gt=timezone.now()) ) def by_value(self, value: str): return self.filter(hash=get_adapter().hash_token(value)) def lookup(self, type, value): return self.valid().by_value(value).filter(type=type).first() class Token(models.Model): objects = TokenQuerySet.as_manager() class Type(models.TextChoices): INITIAL_ACCESS_TOKEN = "ia", "Initial access token" ACCESS_TOKEN = "at", "Access token" REFRESH_TOKEN = "rt", "Refresh token" AUTHORIZATION_CODE = "ac", "Authorization code" type = models.CharField(max_length=2, choices=Type.choices) hash = models.CharField(max_length=255) client = models.ForeignKey(Client, on_delete=models.CASCADE, blank=True, null=True) user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True ) data = models.JSONField(blank=True, null=True, default=None) created_at = models.DateTimeField(default=timezone.now) expires_at = models.DateTimeField(blank=True, null=True, db_index=True) scopes = models.TextField(default="") class Meta: unique_together = (("type", "hash"),) def __str__(self) -> str: if self.user_id: return f"{self.get_type_display()} for user #{self.user_id}" return self.get_type_display() def get_scopes(self) -> list[str]: return _values_from_text(self.scopes) def set_scopes(self, scopes: list[str]) -> None: self.scopes = _values_to_text(scopes) def set_scope_email(self, email: str) -> None: """ In case a specific email was chosen to be exposed to the client, store that using this method. """ if self.data is None: self.data = {} self.data["email"] = email def get_scope_email(self) -> str | None: """ Returns the email that was selected when the email scope was granted. Note that this may e outdated, as the user can change email addresses at any time. """ if not isinstance(self.data, dict): return None return self.data.get("email") ================================================ FILE: allauth/idp/oidc/urls.py ================================================ from django.urls import include, path from allauth.idp.oidc import app_settings, views app_name = "oidc" _api_urlpatterns = [ path( "token", views.token, name="token", ), path( "revoke", views.revoke, name="revoke", ), path( "device/code", views.device_code, name="device_code", ), ] if not app_settings.USERINFO_ENDPOINT: _api_urlpatterns.append( path( "userinfo", views.user_info, name="userinfo", ) ) urlpatterns = [ path( ".well-known/", include( [ path( "openid-configuration", views.configuration, name="configuration", ), path( "jwks.json", views.jwks, name="jwks", ), ] ), ), path( "identity/", include( [ path( "o/", include( [ path( "authorize", views.authorization, name="authorization", ), path( "device", views.device_authorization, name="device_authorization", ), path( "logout", views.logout, name="logout", ), path("api/", include(_api_urlpatterns)), ] ), ) ] ), ), ] ================================================ FILE: allauth/idp/oidc/views.py ================================================ from http import HTTPStatus from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth.decorators import login_required from django.contrib.auth.models import AbstractBaseUser from django.contrib.sites.shortcuts import get_current_site from django.core.exceptions import PermissionDenied from django.core.signing import BadSignature, Signer from django.http import ( HttpRequest, HttpResponse, HttpResponseForbidden, HttpResponseRedirect, JsonResponse, ) from django.middleware.csrf import CsrfViewMiddleware from django.shortcuts import render from django.urls import reverse from django.utils.decorators import method_decorator from django.utils.http import urlencode from django.views import View from django.views.decorators.clickjacking import xframe_options_deny from django.views.decorators.csrf import csrf_exempt from django.views.generic.edit import FormView from oauthlib.oauth2.rfc6749 import errors from oauthlib.oauth2.rfc6749.errors import InvalidScopeError, OAuth2Error from allauth.account import app_settings as account_settings from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.internal.decorators import login_not_required from allauth.core.internal import jwkkit from allauth.core.internal.httpkit import add_query_params, del_query_params from allauth.idp.oidc import app_settings from allauth.idp.oidc.adapter import get_adapter from allauth.idp.oidc.forms import ( AuthorizationForm, ConfirmCodeForm, DeviceAuthorizationForm, RPInitiatedLogoutForm, ) from allauth.idp.oidc.internal import flows from allauth.idp.oidc.internal.oauthlib import device_codes from allauth.idp.oidc.internal.oauthlib.server import get_device_server, get_server from allauth.idp.oidc.internal.oauthlib.utils import ( convert_response, extract_params, respond_html_error, respond_json_error, ) from allauth.idp.oidc.models import Client from allauth.utils import build_absolute_uri def _enforce_csrf(request) -> HttpResponseForbidden | None: """ Scenario: view is CSRF exempt, but, if this is not a client initial POST request, we do want a properly CSRF protected view. """ reason = CsrfViewMiddleware( get_response=lambda req: HttpResponseForbidden() ).process_view(request, lambda *args, **kwargs: HttpResponse(), (), {}) if reason: return HttpResponseForbidden(f"CSRF Failed: {reason}") return None @method_decorator(login_not_required, name="dispatch") class ConfigurationView(View): def get(self, request) -> JsonResponse: userinfo_endpoint = app_settings.USERINFO_ENDPOINT if not userinfo_endpoint: userinfo_endpoint = build_absolute_uri( request, reverse("idp:oidc:userinfo") ) data = { "authorization_endpoint": build_absolute_uri( request, reverse("idp:oidc:authorization") ), "device_authorization_endpoint": build_absolute_uri( request, reverse("idp:oidc:device_code") ), "revocation_endpoint": build_absolute_uri( request, reverse("idp:oidc:revoke") ), "token_endpoint": build_absolute_uri(request, reverse("idp:oidc:token")), "userinfo_endpoint": userinfo_endpoint, "end_session_endpoint": build_absolute_uri( request, reverse("idp:oidc:logout") ), "jwks_uri": build_absolute_uri(request, reverse("idp:oidc:jwks")), "issuer": get_adapter().get_issuer(), "response_types_supported": self._get_response_types_supported(), "subject_types_supported": ["public"], "id_token_signing_alg_values_supported": ["RS256"], } response = JsonResponse(data) response["Access-Control-Allow-Origin"] = "*" return response def _get_response_types_supported(self) -> list[str]: response_types = set() for client in Client.objects.only("response_types").iterator(): response_types.update(client.get_response_types()) return list(sorted(response_types)) configuration = ConfigurationView.as_view() @method_decorator(xframe_options_deny, name="dispatch") @method_decorator(csrf_exempt, name="dispatch") @method_decorator(login_not_required, name="dispatch") class AuthorizationView(FormView): form_class = AuthorizationForm template_name = f"idp/oidc/authorization_form.{account_settings.TEMPLATE_EXTENSION}" def get(self, request, *args, **kwargs) -> HttpResponse: response = self._login_required(request) if response: return response orequest = extract_params(self.request) try: server = get_server() self._scopes, self._request_info = server.validate_authorization_request( *orequest ) if "none" in self._request_info.get("prompt", ()): oresponse = server.create_authorization_response( *orequest, scopes=self._scopes ) return convert_response(*oresponse) # Errors that should be shown to the user on the provider website except errors.FatalClientError as e: return respond_html_error(request, error=e) except errors.OAuth2Error as e: return HttpResponseRedirect(e.in_uri(e.redirect_uri)) if self._request_info["request"].client.skip_consent: return self._skip_consent() return super().get(request, *args, **kwargs) def post(self, request, *args, **kwargs) -> HttpResponse: signed_request_info = request.POST.get("request") if not signed_request_info: return HttpResponseRedirect( f"{reverse('idp:oidc:authorization')}?{request.POST.urlencode()}" ) response = self._login_required(request) if response: return response csrf_resp = _enforce_csrf(request) if csrf_resp: return csrf_resp try: signer = Signer() self._scopes, self._request_info = signer.unsign_object(signed_request_info) except BadSignature: raise PermissionDenied if request.POST.get("action") != "grant": return self._respond_with_access_denied() return super().post(request, *args, **kwargs) def _login_required(self, request) -> HttpResponse | None: prompts = [] prompt = request.GET.get("prompt") if prompt: prompts = prompt.split() if "login" in prompts: return self._handle_login_prompt(request, prompts) if "none" in prompts: return None if request.user.is_authenticated: return None return login_required()(None)(request) # type:ignore[misc,type-var] def _handle_login_prompt( self, request: HttpRequest, prompts: list[str] ) -> HttpResponse: prompts.remove("login") next_url = request.get_full_path() if prompts: next_url = add_query_params(next_url, {"prompt": " ".join(prompts)}) else: next_url = del_query_params(next_url, "prompt") params = {} params[REDIRECT_FIELD_NAME] = next_url path = reverse( "account_reauthenticate" if request.user.is_authenticated else "account_login" ) return HttpResponseRedirect(add_query_params(path, params)) def _skip_consent(self): scopes = self._request_info["request"].scopes form_kwargs = self.get_form_kwargs() form_kwargs["data"] = { "scopes": scopes, "request": "not-relevant-for-skip-consent", } form = self.form_class(**form_kwargs) if not form.is_valid(): # Shouldn't occur. raise PermissionDenied() return self.form_valid(form) def _respond_with_access_denied(self): redirect_uri = self._request_info.get("redirect_uri") state = self._request_info.get("state") params = {"error": "access_denied"} if state: params["state"] = state return HttpResponseRedirect(add_query_params(redirect_uri, params)) def get_form_kwargs(self) -> dict: ret = super().get_form_kwargs() ret.update({"requested_scopes": self._scopes, "user": self.request.user}) return ret def get_initial(self) -> dict: signer = Signer() ret = {} request_info = self._request_info request_info.pop("request", None) prompt = request_info.get("prompt") if isinstance(prompt, set): request_info["prompt"] = list(prompt) ret["request"] = signer.sign_object((self._scopes, request_info)) return ret def form_valid(self, form) -> HttpResponse: orequest = extract_params(self.request) scopes = form.cleaned_data["scopes"] credentials = {"user": self.request.user} credentials.update(self._request_info) try: email = form.cleaned_data.get("email") if email: credentials["email"] = email oresponse = get_server().create_authorization_response( *orequest, scopes=scopes, credentials=credentials ) return convert_response(*oresponse) except errors.FatalClientError as e: return respond_html_error(self.request, error=e) def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) ret.update( { "client": Client.objects.get(id=self._request_info["client_id"]), "site": get_current_site(self.request), } ) return ret authorization = AuthorizationView.as_view() @method_decorator(csrf_exempt, name="dispatch") @method_decorator(login_not_required, name="dispatch") class DeviceCodeView(View): def post(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: orequest = extract_params(request) try: headers, data, status = ( get_device_server().create_device_authorization_response(*orequest) ) if status == HTTPStatus.OK: client_id = request.POST["client_id"] scope: list[str] | None = None if "scope" in request.POST: scope = request.POST["scope"].split() client = Client.objects.get(id=client_id) if not set(scope).issubset(set(client.get_scopes())): raise InvalidScopeError() device_codes.create(client_id, scope, data) except OAuth2Error as e: return HttpResponse( e.json, content_type="application/json", status=e.status_code ) return convert_response(headers, data, status) device_code = DeviceCodeView.as_view() @method_decorator(csrf_exempt, name="dispatch") @method_decorator(login_required, name="dispatch") class DeviceAuthorizationView(View): def dispatch(self, request, *args, **kwargs) -> HttpResponse: if "code" in request.GET: form = ConfirmCodeForm(request.GET) if form.is_valid(): return self._dispatch_authorization( request, form.cleaned_data["code"], form.device_code, form.client, ) else: form = ConfirmCodeForm() context = { "form": form, "autorization_url": reverse("idp:oidc:device_authorization"), } return render( request, f"idp/oidc/device_authorization_code_form.{account_settings.TEMPLATE_EXTENSION}", context, ) def _dispatch_authorization( self, request, user_code: str, device_code: str, client: Client ): context = {"user_code": user_code, "client": client} if request.method == "POST": form = DeviceAuthorizationForm(request.POST) if form.is_valid(): confirm = form.cleaned_data["action"] == "confirm" device_codes.confirm_or_deny_device_code( request.user, device_code, confirm=confirm ) if confirm: template_name = f"idp/oidc/device_authorization_confirmed.{account_settings.TEMPLATE_EXTENSION}" else: template_name = f"idp/oidc/device_authorization_denied.{account_settings.TEMPLATE_EXTENSION}" return render(request, template_name, context) else: form = DeviceAuthorizationForm() context["autorization_url"] = ( reverse("idp:oidc:device_authorization") + "?" + urlencode({"code": user_code}) ) return render( request, f"idp/oidc/device_authorization_confirm_form.{account_settings.TEMPLATE_EXTENSION}", context, ) device_authorization = DeviceAuthorizationView.as_view() @method_decorator(csrf_exempt, name="dispatch") @method_decorator(login_not_required, name="dispatch") class TokenView(View): def post(self, request) -> HttpResponse: if request.POST.get("grant_type") == Client.GrantType.DEVICE_CODE: return self._post_device_token(request) return self._create_token_response(request) def _create_token_response( self, request, *, user: AbstractBaseUser | None = None, data: dict | None = None, ): orequest = extract_params(request) oresponse = get_server( pre_token=[lambda orequest: self._pre_token(orequest, user, data)] ).create_token_response(*orequest) return convert_response(*oresponse) def _pre_token(self, orequest, user: AbstractBaseUser | None, data: dict | None): if orequest.grant_type == Client.GrantType.DEVICE_CODE: assert user is not None # nosec assert data is not None # nosec if scope := data.get("scope"): orequest.scope = scope orequest.user = user def _post_device_token(self, request): try: user, data = device_codes.poll_device_code(request) except OAuth2Error as e: return HttpResponse( e.json, content_type="application/json", status=e.status_code ) else: return self._create_token_response(request, user=user, data=data) token = TokenView.as_view() @method_decorator(csrf_exempt, name="dispatch") @method_decorator(login_not_required, name="dispatch") class UserInfoView(View): """ The UserInfo Endpoint MUST support the use of the HTTP GET and HTTP POST methods """ def get(self, request: HttpRequest) -> HttpResponse: return self._respond(request) def post(self, request: HttpRequest) -> HttpResponse: return self._respond(request) def _respond(self, request: HttpRequest) -> HttpResponse: orequest = extract_params(request) try: oresponse = get_server().create_userinfo_response(*orequest) return convert_response(*oresponse) except OAuth2Error as e: return respond_json_error(request, e) user_info = UserInfoView.as_view() @method_decorator(login_not_required, name="dispatch") class JwksView(View): def get(self, request, *args, **kwargs) -> JsonResponse: keys = [] for pem in [app_settings.PRIVATE_KEY]: jwk, _ = jwkkit.load_jwk_from_pem(pem) keys.append(jwk) response = JsonResponse({"keys": keys}) response["Access-Control-Allow-Origin"] = "*" return response jwks = JwksView.as_view() @method_decorator(csrf_exempt, name="dispatch") @method_decorator(login_not_required, name="dispatch") class RevokeView(View): def post(self, request, *args, **kwargs) -> HttpResponse: orequest = extract_params(request) oresponse = get_server().create_revocation_response(*orequest) return convert_response(*oresponse) revoke = RevokeView.as_view() @method_decorator(csrf_exempt, name="dispatch") @method_decorator(login_not_required, name="dispatch") class LogoutView(FormView): """ https://openid.net/specs/openid-connect-rpinitiated-1_0.html """ form_class = RPInitiatedLogoutForm template_name = f"idp/oidc/logout.{account_settings.TEMPLATE_EXTENSION}" def get(self, request) -> HttpResponse: form = self.form_class(request.GET) if not form.is_valid(): return self.form_invalid(form) if not self._must_ask(form): return self._handle(form, True) return self.render_to_response(self.get_context_data(form=form)) def form_invalid(self, form) -> HttpResponse: return respond_html_error(self.request, form=form) def form_valid(self, form) -> HttpResponse: ask = self._must_ask(form) action = form.cleaned_data["action"] if ask: # If we're supposed to ask, we need to ensure this POST request does # NOT come from the RP, but from the actual user visitting the logout # page. csrf_token = self.request.POST.get("csrfmiddlewaretoken", "") if not csrf_token or not action: return self.render_to_response(self.get_context_data(form=form)) csrf_resp = _enforce_csrf(self.request) if csrf_resp: return csrf_resp op_logout = action != "stay" else: op_logout = True return self._handle(form, op_logout) def _handle( self, form: RPInitiatedLogoutForm, op_logout: bool ) -> HttpResponseRedirect: cleaned_data = form.cleaned_data flows.rp_initiated_logout( self.request, from_op=op_logout, client=cleaned_data["client"], post_logout_redirect_uri=cleaned_data["post_logout_redirect_uri"], ) redirect_uri = cleaned_data["post_logout_redirect_uri"] if redirect_uri: state = cleaned_data["state"] if state: redirect_uri = add_query_params(redirect_uri, {"state": state}) else: redirect_uri = get_account_adapter().get_logout_redirect_url(self.request) return HttpResponseRedirect(redirect_uri) def _must_ask(self, form: RPInitiatedLogoutForm) -> bool: """ At the Logout Endpoint, the OP SHOULD ask the End-User whether to log out of the OP as well. Furthermore, the OP MUST ask the End-User this question if an id_token_hint was not provided or if the supplied ID Token does not belong to the current OP session with the RP and/or currently logged in End-User. If the End-User says "yes", then the OP MUST log out the End-User. """ if self.request.user.is_anonymous: return False if app_settings.RP_INITIATED_LOGOUT_ASKS_FOR_OP_LOGOUT: return True id_token_hint = form.cleaned_data["id_token_hint"] sub = None if id_token_hint: sub = id_token_hint.get("sub") client = form.cleaned_data.get("client") if not id_token_hint or not client or not sub: return True user_hint = get_adapter().get_user_by_sub(client, sub) if not user_hint or (user_hint.pk != self.request.user.pk): return True return False logout = LogoutView.as_view() ================================================ FILE: allauth/idp/urls.py ================================================ from django.urls import include, path app_name = "idp" urlpatterns = [ path("", include("allauth.idp.oidc.urls")), ] ================================================ FILE: allauth/locale/ar/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-11-28 05:00+0000\n" "Last-Translator: Mohammed “Medait” AIT ALI \n" "Language-Team: Arabic \n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " "&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" "X-Generator: Weblate 5.15-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "هذا الحساب غير نشط حاليا." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "لا يمكنك إزالة عنوان بريدك الإلكتروني الرئيسي." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "عنوان البريد الإلكتروني هذا مربوط بالفعل مع هذا الحساب." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "عنوان البريد الإلكتروني و / أو كلمة المرور غير صحيحة." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "رقم الهاتف و / أو كلمة المرور غير صحيحة." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "هنالك مستخدم مسجل سابقا يستخدم عنوان البريد الإلكتروني نفسه." #: account/adapter.py:75 msgid "Please type your current password." msgstr "الرجاء كتابة كلمة المرور الحالية." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "كود خاطئ." #: account/adapter.py:77 msgid "Incorrect password." msgstr "كلمة مرور خاطئة." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "مفتاح خاطئ او منتهي." #: account/adapter.py:79 msgid "Invalid login." msgstr "فشل تسجيل الدخول." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "كود إعادة تعيين كلمة المرور غير صالح." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "لا يمكنك إضافة أكثر من %d بريد إلكتروني." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "تم تسجيل مستخدم بالفعل برقم الهاتف هذا." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "تجاوزت الحد المسموح لمحاولة تسجيل الدخول. حاول في وقت لاحق." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "لم يتم ربط عنوان البريد الإلكتروني مع أي حساب مستخدم." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "لم يتم ربط رقم الهاتف مع أي حساب مستخدم." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "يجب توثيق عنوان بريدك الإلكتروني الرئيسي." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "اسم المستخدم غير مسموح به. الرجاء اختيار اسم آخر‪." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "اسم المستخدم و / أو كلمة المرور غير صحيحة." #: account/adapter.py:98 msgid "Please select only one." msgstr "الرجاء اختيار واحد فقط." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "يجب أن تكون القيمة الجديدة مختلفة عن القيمة الحالية." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "كن صبوراً، فأنت ترسل عدداً كبيراً جداً من الطلبات." #: account/adapter.py:826 msgid "Use your password" msgstr "استخدم كلمة مرورك" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "استخدم تطبيق المصادقة او الكود" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "استخدم مفتاح الأمان" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "تحديد {email} على أنه تم التحقق منه" #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "فشل تحديد {email} على أنه تم التحقق منه." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "تحديد البريد الإلكتروني المختار بأنه تم التحقق منه" #: account/apps.py:11 msgid "Accounts" msgstr "الحسابات" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "البريد الالكتروني" #: account/fields.py:19 msgid "Email address" msgstr "عنوان البريد الالكتروني" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" "الرجاء إدخال رقم هاتف بما في ذلك رقم البلاد (على سبيل المثال ‪+1‬ للولايات " "المتحدة)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "هاتف" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "يجب عليك كتابة كلمة المرور نفسها في كل مرة‪." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "كلمة المرور" #: account/forms.py:67 msgid "Remember Me" msgstr "تذكرني" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "اسم المستخدم" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "تسجيل الدخول" #: account/forms.py:115 msgid "Username, email or phone" msgstr "اسم المستخدم أو البريد الإلكتروني أو الهاتف" #: account/forms.py:117 msgid "Username or email" msgstr "اسم المستخدم أو البريد الإلكتروني" #: account/forms.py:119 msgid "Username or phone" msgstr "اسم المستخدم أو الهاتف" #: account/forms.py:121 msgid "Email or phone" msgstr "البريد الالكتروني أو الهاتف" #: account/forms.py:144 msgid "Forgot your password?" msgstr "هل نسيت كلمة المرور؟" #: account/forms.py:287 msgid "Email (again)" msgstr "البريد الإلكتروني ‪(مجددا)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "تأكيد البريد الإلكتروني" #: account/forms.py:302 msgid "Email (optional)" msgstr "البريد الالكتروني (اختياري)" #: account/forms.py:314 msgid "Username (optional)" msgstr "اسم المستخدم (اختياري)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "يجب عليك كتابة البريد الإلكتروني نفسه في كل مرة‪." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "كلمة المرور (مجددا)" #: account/forms.py:591 msgid "Current Password" msgstr "كلمة المرور الحالية" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "كلمة المرور الجديدة" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "كلمة المرور الجديدة (مجددا)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "شفرة" #: account/models.py:23 msgid "user" msgstr "مستخدم" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "عنوان بريد إلكتروني" #: account/models.py:31 msgid "verified" msgstr "موثق" #: account/models.py:32 msgid "primary" msgstr "رئيسي" #: account/models.py:38 msgid "email addresses" msgstr "عناوين البريد الالكتروني" #: account/models.py:142 msgid "created" msgstr "تمّ إنشاؤه" #: account/models.py:143 msgid "sent" msgstr "تم ارساله" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "مفتاح" #: account/models.py:149 msgid "email confirmation" msgstr "تأكيد البريد الإلكتروني" #: account/models.py:150 msgid "email confirmations" msgstr "تأكيدات البريد الإلكتروني" #: headless/apps.py:7 msgid "Headless" msgstr "مقطوعة الرأس" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "عرض معرف المستخدم الخاص بك" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "عرض عنوان بريدك الإلكتروني" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "عرض معلومات ملفك الشخصي الأساسية" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "منح الأذونات" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "لا يُسمح باستخدام أحرف البدل (*) ما لم يتم تمكين 'السماح بأحرف البدل في " "عناوين URI'." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "عنوان URI '{}' يحتوي على أكثر من حرف بدل (*). يُسمح بحرف بدل واحد فقط لكل " "عنوان URI." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "لا يُسمح بأحرف البدل (*) إلا في جزء اسم المضيف من عنوان URI." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "رمز التفويض" #: idp/oidc/models.py:38 msgid "Device code" msgstr "رمز الجهاز" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "بيانات اعتماد العميل" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "رمز التحديث" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "سري" #: idp/oidc/models.py:44 msgid "Public" msgstr "عام" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "النطاق (النطاقات) التي يُسمح للعميل بطلبها. قم بتوفير قيمة واحدة لكل سطر، " "مثال: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "في حال لم يحدد العميل أي نطاق، يتم استخدام هذه النطاقات الافتراضية. قم " "بتوفير قيمة واحدة لكل سطر، مثال: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "قائمة أنواع المنح المسموح بها. قم بتوفير قيمة واحدة لكل سطر، مثال: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "قائمة المصادر المسموح بها للطلبات عبر المصادر، واحد لكل سطر." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "السماح بأحرف البدل (*) في عناوين URI لإعادة التوجيه ومصادر CORS. عند " "التمكين، يمكن أن تحتوي عناوين URI على علامة نجمة واحدة لمطابقة النطاقات " "الفرعية." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "قائمة أنواع الاستجابات المسموح بها. قم بتوفير قيمة واحدة لكل سطر، مثال: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "عميل" #: idp/oidc/models.py:116 msgid "clients" msgstr "عملاء" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "لا يمكنك إضافة عنوان بريد إلكتروني إلى حساب محمي بواسطة المصادقة الثنائية." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "لا يمكنك إلغاء تنشيط المصادقة الثنائية." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "لا يمكنك إنشاء رموز الاسترداد دون تمكين المصادقة الثنائية." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "لا يمكنك تفعيل المصادقة الثنائية إلا بعد التحقق من عنوان بريدك الإلكتروني." #: mfa/adapter.py:141 msgid "Master key" msgstr "مفتاح رئيسي" #: mfa/adapter.py:143 msgid "Backup key" msgstr "المفتاح الاحتياطي" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "مفتاح رقم {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "وزارة الخارجية" #: mfa/models.py:24 msgid "Recovery codes" msgstr "رموز الاسترداد" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "أداة مصادقة TOTP" #: mfa/models.py:26 msgid "WebAuthn" msgstr "مصادقة الويب" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "كود المصدق" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "بدون كلمة مرور" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "يسمح لك تفعيل الدخول من غير كلمة مرور باستعمال هذا المفتاح فقط، ولكنه يفرض " "شروط دخول أخرى مثل الدخول بالمعرّفات الحيوية (مثل البصمة) أو رقم التعريف " "الشخصي." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "يوجد حساب بالفعل مربوط مع هذا البريد الإلكتروني. يرجى الدخول إلى ذاك الحساب " "أولا، ثم ربط حسابك في %s." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "كود غير صالح." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "حسابك ليست له كلمة مرور مضبوطة." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "حسابك ليس لديه عنوان بريد إلكتروني موثقف." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "لا يمكنك حذف هذا الحساب لطرف ثالث وفصله عن حسابك." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "حساب الطرف الثالث متصل مسبقا مع حساب مختلف." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "حسابات التواصل الاجتماعي" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "مزود" #: socialaccount/models.py:53 msgid "provider ID" msgstr "معرف المزود" #: socialaccount/models.py:57 msgid "name" msgstr "اسم" #: socialaccount/models.py:59 msgid "client id" msgstr "معرف العميل" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "معرف آبل، أو مفتاح المستهلك" #: socialaccount/models.py:64 msgid "secret key" msgstr "مفتاح سري" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "مفتاح واجهة برمجية سري أو مفتاح مستهلك" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "مفتاح" #: socialaccount/models.py:82 msgid "social application" msgstr "تطبيق اجتماعي" #: socialaccount/models.py:83 msgid "social applications" msgstr "تطبيقات اجتماعية" #: socialaccount/models.py:118 msgid "uid" msgstr "معرف المستخدم" #: socialaccount/models.py:120 msgid "last login" msgstr "أخر دخول" #: socialaccount/models.py:121 msgid "date joined" msgstr "تاريخ الانضمام" #: socialaccount/models.py:122 msgid "extra data" msgstr "بيانات إضافية" #: socialaccount/models.py:126 msgid "social account" msgstr "حساب تواصل اجتماعي" #: socialaccount/models.py:127 msgid "social accounts" msgstr "حسابات تواصل اجتماعي" #: socialaccount/models.py:161 msgid "token" msgstr "كود" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) أو مفتاح وصول (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "كود سري" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) أو رمز تحديث (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "ينتهي في" #: socialaccount/models.py:175 msgid "social application token" msgstr "كود تطبيق اجتماعي" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "أكواد التطبيقات الاجتماعية" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "معلومات ملف شخصي غير صالحة" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "تسجيل الدخول" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "يلغي" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "تم تلقي رد غير صالح عند الحصول على كود الطلب من \"%s\". الرد كان: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "تم تلقي رد غير صالح عند الحصول على كود الوصول من \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "لا يوجد كود طلب محفوظ لـ \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "لا يوجد كود وصول محفوظ لـ \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "لا وصول للموارد الخاصة في \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "تم تلقي رد غير صالح عند الحصول على كود الطلب من \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "الحساب غير نشط" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "هذا الحساب غير نشط." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "لقد أرسلنا رمزًا إلى %(recipient)s. الرمز ينتهي قريبًا، لذا يرجى إدخاله قريبًا." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "تأكيد" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "طلب كود جديد" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "تأكيد الوصول" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "يرجى إعادة المصادقة لحماية حسابك." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "خيارات بديلة" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "تأكيد البريد الإلكتروني" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "الرجاء إدخال الكود المبعوث إلى بريدك الالكتروني" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "استخدم بريد إلكتروني آخر" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "تسجيل الدخول" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "أدخل رمز تسجيل الدخول" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "إعادة تعيين كلمة المرور" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "الرجاء إدخال الكود لإعادة تعيين كلمة المرور" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "التأكيد من رقم الهاتف" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "الرجاء إدخال الكود للتأكيد من رقم الهاتف" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "استخدم رقم هاتف آخر" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "عناوين البريد الإلكتروني" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "عناوين البريد الإلكتروني التالية مربوطة مع حسابك:" #: templates/account/email.html:25 msgid "Verified" msgstr "موثق" #: templates/account/email.html:29 msgid "Unverified" msgstr "غير موثق" #: templates/account/email.html:34 msgid "Primary" msgstr "أساسي" #: templates/account/email.html:44 msgid "Make Primary" msgstr "اجعله أساسيا" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "إعادة ارسال رسالة التأكيد" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "احذف" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "أضف عنوان بريد إلكتروني" #: templates/account/email.html:70 msgid "Add Email" msgstr "أضف بريدا إلكترونيا" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "هل تريد حقا حذف عنوان البريد الإلكتروني المحدد؟" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "تلقيت هذه الرسالة لأنك أو أحد آخر حاول تسجيل حساب\n" "باستخدام البريد الإلكتروني:\n" "\n" "%(email)s\n" "\n" "لكن حسابا مربوطا بهذا البريد موجود بالفعل. \n" "في حال نسيت هذا، يمكنك استخدام رابط\n" "إعادة ضبط كلمة المرور لاستعادة حسابك:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "الحساب موجود بالفعل" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "مرحبا من موقع %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "شكرا لاستخدامك %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "أنت تتلقى هذا البريد لأنه تم إجراء التغيير التالي على حسابك:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "إذا لم تتعرف على هذا التغيير، فيرجى اتخاذ الاحتياطات الأمنية المناسبة على " "الفور. التغيير في حسابك ناتج عن:\n" "\n" "- عنوان IP: %(ip)s\n" "- المتصفح: %(user_agent)s\n" "- التاريخ: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "J لقد تم تغيير بريدك الإلكتروني من %(from_email)s إلى %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "تم تغيير عنوان البريد الالكتروني" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "تم التأكيد من عنوان بريدك الالكتروني." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "تأكيد البريد الإلكتروني" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "تلقيت هذه الرسالة لأن المستخدم %(user_display)s أعطانا بريدك الإلكتروني " "لتسجيل حساب في %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "الكود للتأكيد من بريدك الالكتروني مدرج أدناه. الرجاء إدخاله في نافذة المتصفح " "المفتوحة." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "للتأكيد بأن هذا صحيح، اذهب إلى %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "أكد عنوان البريد الإلكتروني" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "تمت إزالة عنوان البريد الإلكتروني %(deleted_email)s من حسابك." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "تم حذف البريد الالكتروني" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "رمز تسجيل الدخول الخاص بك مدرج أدناه. الرجاء إدخاله في نافذة المتصفح " "المفتوحة." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "يمكن تجاهل هذا البريد بأمان إذا لم تبدأ هذا الإجراء." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "كود تسجيل الدخول" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "تم تغيير كلمة المرور الخاصة بك." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "تم تغيير كلمة المرور" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "الكود لإعادة تعيين كلمة المرور مدرج أدناه. الرجاء إدخاله في نافذة المتصفح " "المفتوحة." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "كود إعادة تعيين كلمة المرور" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "تلقيت هذه الرسالة لأنك أو أحد آخر طلب إعادة ضبط كلمة مرور حسابك.\n" "يمكنك تجاهل الرسالة بأمان إذا لم تطلب هذا. اضغط الرابط في الأسفل لإعادة ضبط " "كلمة المرور." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "في حال كنت قد نسيت، اسم المستخدم الخاص بك هو %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "رسالة إعادة ضبط كلمة المرور" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "تم إعادة تعيين كلمة المرور الخاصة بك." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "تم تحديد كلمة المرور الخاصة بك." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "تم تحديد كلمة المرور" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "O أنت تتلقى هذا البريد الإلكتروني لأنك، أو حاول شخص آخر، الوصول إلى حساب " "باستخدام البريد الإلكتروني %(email)s. ومع ذلك، ليس لدينا أي سجل لمثل هذا " "الحساب في قاعدة بياناتنا." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "إذا كنت أنت، فيمكنك التسجيل للحصول على حساب باستخدام الرابط أدناه." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "حساب غير معروف" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "بريد إلكتروني" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "البريد الالكتروني الحالي" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "التغيير إلى" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "ما زال عنوان البريد الإلكتروني الخاص بك بانتظار التوثيق." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "إلغاء التغيير" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "التغيير إلى" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "تغيير البريد الإلكتروني" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "تأكيد البريد الإلكتروني" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "يرجى تأكيد أن %(email)s هو عنوان بريد " "إلكتروني للمستخدم %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "فشل تأكيد %(email)s لأنه مؤكد بالفعل في حساب مختلف." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "هذا الرابط لتأكيد البريد الإلكتروني نتهت فترة صلاحيته أو إنه غير صالح. يرجى " "طلب رابط جديد لتأكيد البريد الإلكتروني." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "إذا لم يكن لديك حساب، يرجى %(link)sالتسجيل%(end_link)s أولا." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "تسجيل الدخول باستعمال مفتاح مرور" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "أرسل لي كود تسجيل الدخول" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "تسجيل الخروج" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "هل تريد حقا تسجيل الخروج؟" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "لا يمكنك إزالة عنوان البريد الإلكتروني الرئيسي (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "تم إرسال رسالة تأكيد إلى العنوان %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "لقد أكدت عنوان البريد الإلكتروني %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "تمت إزالة عنوان البريد الإلكتروني %(email)s." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "لقد سجلت الدخول بنجاح يا %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "تم تسجيل خروجك." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "تم إرسال كود تسجيل الدخول إلى %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "تم تغيير كلمة المرور بنجاح‪." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "تم إعداد كلمة المرور بنجاح‪." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "تم إرسال رمز التحقق إلى %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "لقد تحققت من رقم الهاتف %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "تم تعيين عنوان البريد الإلكتروني الرئيسي‪." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "غيّر كلمة المرور" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "هل نسيت كلمة المرور؟" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "هل نسيت كلمة المرور؟ أدخل عنوان البريد الإلكتروني أدناه، وسنرسل لك رسالة " "تتيح لك إعادة تعيينها." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "إعادة تعيين كلمة المرور" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "يرجى الاتصال بنا إذا كنت تواجه أي مشاكل في إعادة تعيين كلمة المرور." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "أرسلنا لك رسالة. إذا لم تصلك يرجى التحقق من صندوق الرسائل غير المرغوب فيها. " "اتصل بنا إذا لم تصل في غضون دقائق." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "كود غير صالح" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "رابط إعادة تعيين كلمة المرور غير صالح، ربما لأنه قد تم استخدامه مسبقا. يرجى " "طلب رابط جديد." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "تم تغيير كلمة مرورك الآن." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "تعيين كلمة مرور" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "تغيير الهاتف" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "رقم الهاتف الحالي" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "ما زال رقم الهاتف الخاص بك بانتظار التوثيق." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "تغيير الهاتف" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "أدخل كلمة المرور الخاص بك:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "ستتلقى كود خاص لتسجيل الدخول بدون كلمة مرور." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "كود الطلب" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "خيارات تسجيل الدخول الأخرى" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "التسجيل" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "الاشتراك" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "لديك حساب؟ %(link)sسجل الدخول%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "تسجيل باستعمال مفتاح مرور" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "التسجيل عن خلال مفتاح مرور" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "خيارات أخرى" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "الاشتراك مغلق" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "نحن آسفون‪:‬ الاشتراك مغلق حاليا‪." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "ملاحظة" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "تم تسجيل دخولك بالفعل يا %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "تحذير‪:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "ليس لديك أي بريد إلكتروني مضبوط. عليك حقا إضافة عنوان لكي تتلقى الإشعارات، " "وتعيد تعيين كلمة المرور، إلخ." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "التحقق من البريد الإلكتروني" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "لقد أرسلنا لك رسالة للتوثيق. اتبع الرابط المزود لإكمال عملية التسجيل. إذا لم " "ترى الرسالة في صندوقك الرئيسي، تحقق من صندوق الرسائل غير المرغوب فيها. يرجى " "التواصل معنا إذا لم تتلقى الرسالة في غضو دقائق." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "تتطلب هذه الصفحة توثيق هويتك.\n" "لهذا نطلب منك توثيق ملكية\n" " بريدك الإلكتروني. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "أرسلنا لك رسالة للتوثيق.\n" "يرجى الضغط على الرابط في الرسالة. إذا لم ترى الرسالة في صندوقك الرئيسي، تحقق " "من صندوق الرسائل غير المرغوب فيها.\n" "يرجى التواصل معنا إذا لم تتلقاه في غضون دقائق." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "ملاحظة: ما زال بإمكانك تغيير " "البريد الإلكتروني." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "رسائل:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "قائمة:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "اتصالات الحساب" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "توثيق ذو عاملين" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "الجلسات" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "تفويض" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s يريد الوصول إلى حسابك في %(site_name)s." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "أدخل رمز الجهاز" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "أدخل الرمز المعروض على جهازك." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "المتابعة" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "تأكيد الجهاز" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "يرجى تأكيد الرمز المعروض على %(client_name)s الخاص بك لتفويض هذا الجهاز." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "رفض" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "تم تفويض الجهاز" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "لقد قمت بتفويض جهاز %(client_name)s الخاص بك بنجاح." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "تم رفض الجهاز" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "تم رفض التفويض لجهاز %(client_name)s الخاص بك." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "خطأ" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "البقاء مسجل الدخول" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "حسابك محمي بواسطة المصادقة الثنائية. الرجاء إدخال رمز المصادقة:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "تم إنشاء مجموعة جديدة من رموز استرداد المصادقة الثنائية." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "تم إنشاء رموز استرداد جديدة" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "تم تفعيل تطبيق المصادقة." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "تم تفعيل تطبيق المصادقة" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "تم تعطيل تطبيق المصادقة." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "تم تعطيل تطبيق المصادقة" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "أضيف مفتاح أمان جديد." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "تم إضافة مفتاح أمان" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "تم حذف مفتاح أمان." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "تم حذف مفتاح أمان" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "تطبيق المصادقة" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "المصادقة باستخدام تطبيق المصادقة نشطة." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "تطبيق المصادقة غير نشط." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "إلغاء التنشيط" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "تفعيل" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "مفاتيح أمان" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "أضفت %(count)s مفتاح أمان." msgstr[1] "أضفت مفتاح أمان واحد." msgstr[2] "أضفت مفتاحين أمان." msgstr[3] "أضفت %(count)s مفاتيح أمان." msgstr[4] "أضفت %(count)s مفتاح أمان." msgstr[5] "أضفت %(count)s مفتاح أمان." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "لم تُضاف مفاتيح أمان." #: templates/mfa/index.html:62 msgid "Manage" msgstr "إدارة" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "إضافة" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "رموز الاسترداد" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "هناك 6(unused_count)s من %(total_count)s رموز الاسترداد المتاحة." msgstr[1] "هناك 6(unused_count)s من %(total_count)s رموز الاسترداد المتاحة." msgstr[2] "هناك %(unused_count)s من %(total_count)s رموز الاسترداد المتاحة." msgstr[3] "هناك %(unused_count)s من %(total_count)s رموز الاسترداد المتاحة." msgstr[4] "هناك %(unused_count)s من %(total_count)s رموز الاسترداد المتاحة." msgstr[5] "هناك %(unused_count)s من %(total_count)s رموز الاسترداد المتاحة." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "لم يتم إعداد رموز الاسترداد." #: templates/mfa/index.html:96 msgid "View" msgstr "عرض" #: templates/mfa/index.html:102 msgid "Download" msgstr "تحميل" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "يولد" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "تم إنشاء مجموعة جديدة من رموز الاسترداد." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "تم إضافة مفتاح أمان." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "تم حذف مفتاح أمان." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "أدخل كود تطبيق المصادقة:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "أنت على وشك إنشاء مجموعة جديدة من رموز الاسترداد لحسابك." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "سيؤدي هذا الإجراء إلى إبطال رموزك الحالية." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "هل أنت متأكد؟" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "رموز غير مستخدمة" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "تنزيل الرموز" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "توليد رموز جديدة" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "تفعيل تطبيق المصادقة" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "لحماية حسابك من خلال المصادقة الثنائية، قم بمسح رمز الاستجابة السريعة أدناه " "باستخدام تطبيق المصادقة الخاص بك. ثم أدخل رمز التحقق الذي أنشأه التطبيق " "أدناه." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "سر تطبيق المصادقة" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "يمكنك تخزين هذا السر واستخدامه لإعادة تثبيت تطبيق المصادقة الخاص بك في وقت " "لاحق." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "قم بإلغاء تنشيط تطبيق المصادقة" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "أنت على وشك إلغاء تنشيط المصادقة المستندة إلى تطبيق المصادقة. هل أنت متأكد؟" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "هل تثق بهذا المتصفح؟" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "إذا اخترت الوثوق بهذا المتصفح، فلن يُطلب منك رمز التحقق في المرة التالية التي " "تقوم فيها بتسجيل الدخول." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "الثقة لفترة %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "لا تثق" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "إضافة مفتاح أمان" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "إزالة مفتاح أمان" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "هل أنت متأكد أنك تريد إزالة مفتاح الأمان هذا؟" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "الاستخدام" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "مفتاح المرور" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "مفتاح أمان" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "هذا المفتاح لا يشير إلى ما إذا كان مفتاح المرور." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "غير محدد" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "تاريخ الإضافة: %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "تاريخ آخر استعمال: %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "تحرير" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "تغيير مفتاح الأمان" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "حفظ" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "إنشاء مفتاح مرور" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "أنت على وشك أن تنشأ مفتاح مرور لحسابك. يمكنك تعيين اسم وصفي لهذا المفتاح " "لتمييزه عن أي مفتاح مرور تضيفه لاحقا." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "إنشاء" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "تحتاج هذه العملية استعمال جافاسكرِبت." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "فشل تسجيل الدخول بحساب طرف ثالث" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "حدث خطأ أثناء محاولة تسجيل الدخول عن طريق حساب طرف ثالث." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "يمكنك تسجيل الدخول إلى حسابك باستخدام أي من حسابات الطرف الثالث التالية:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "ليس لديك حاليا أي حساب طرف ثالث متصل بهذا الحساب." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "إضافة حساب طرف ثالث" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "تم ربط حساب جهة خارجية من %(provider)s بحسابك." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "تم إضافة حساب طرف ثالث" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "تم قطع اتصال حساب جهة خارجية من %(provider)s بحسابك." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "تم إزالة حساب طرف ثالث" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "ربط %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "أنت على وشك ربط حساب طرف ثالث جديد من %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "تسجيل الدخول عبر %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "أنت على وشك تسجيل الدخول باستخدام حساب طرف ثالث من %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "تم إلغاء تسجيل الدخول" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "قد قررت إلغاء تسجيل الدخول إلى الموقع باستخدام أحد الحسابات الموجودة الخاصة " "بك. إذا كان هذا خطأ، الرجاء المتابعة إلى تسجيل " "الدخول." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "تم ربط حساب طرف ثالث." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "تم قطع الاتصال بحساب الطرف الثالث." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "أنت على وشك استخدام حسابك من %(provider_name)s لتسجيل الدخول إلى " "%(site_name)s.\n" "كخطوة أخيرة، يرجى ملء النموذج التالي:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "أو استخدم طرفًا ثالثًا" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "تم تسجيل الخروج من جميع الجلسات الأخرى." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "بدأت في" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "عنوان IP" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "المتصفح" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "شوهد آخر مرة في" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "حالي" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "تسجيل الخروج من الجلسات الأخرى" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "جلسات المستخدم" #: usersessions/models.py:94 msgid "session key" msgstr "مفتاح جلسة" #, fuzzy #~ msgid "Account Connection" #~ msgstr "اتصالات الحساب" #~ msgid "Use security key or device" #~ msgstr "استخدم مفتاح او جهاز الحماية" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "كلمة المرور يجب أن لا تقل عن {0} حروف." #, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "تلقيت هذه الرسالة لأنك أو أحد آخر طلب كلمة لحسابك.\n" #~ "لكن ليس لدينا قيد مستخدم يستخدم عنوان البريد الإلكتروني %(email)s\n" #~ "في قاعدة بياناتنا.\n" #~ "\n" #~ "يمكنك تجاهل هذه الرسالة بأمان إذا لم تطلب إعادة ضبط كلمة المرور.\n" #~ "\n" #~ "إذا كنت أنت من طلب، يمكنك تسجيل حساب باستخدام الرابط في الأسفل." #~ msgid "The following email address is associated with your account:" #~ msgstr "عناوين البريد الإلكتروني التالية مربوطة مع حسابك:" #~ msgid "Change Email Address" #~ msgstr "تغيير البريد الإلكتروني" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "يرجى تسجيل الدخول مع واحد من حسابات الطرف الثالث الموجودة لديك‪.‬\n" #~ "كما يمكنك تسجيل حساب\n" #~ "في موقع %(site_name)s والدخول في الأسفل:" #~ msgid "or" #~ msgstr "أو" #~ msgid "change password" #~ msgstr "تغيير كلمة المرور" #~ msgid "OpenID Sign In" #~ msgstr "تسجيل الدخول عبر OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "عنوان البريد الإلكتروني هذا مقترن بالفعل بحساب آخر." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "لقد قمنا بإرسال رسالة اليك عبر البريد الإلكتروني. يرجى الاتصال بنا اذا " #~ "كنت لا تتلقى في غضون بضع دقائق." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "تسجيل الدخول و / أو كلمة المرور الذي حددته غير صحيحة." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "يمكن أن تحتوي أسماء المستخدمين إلا على الحروف، الأرقام و @/‪.‬/-/+/_." #~ msgid "This username is already taken. Please choose another." #~ msgstr "اسم المستخدم مسجل مسبقا. الرجاء اختيار اسم اخر‪.‬" #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "لقد اكّدت ان %(email)s هو من إحدى عناوين " #~ "للمستعمل %(user_display)s." #~ msgid "Thanks for using our site!" #~ msgstr "شكرا لاستخدام موقعنا!" #~ msgid "Confirmation email sent to %(email)s" #~ msgstr "ارسلت رسالة التأكيد الى بريدك الالكتروني %(email)s" #~ msgid "Delete Password" #~ msgstr "احذف كلمة المرور" #~ msgid "" #~ "You may delete your password since you are currently logged in using " #~ "OpenID." #~ msgstr "بامكانك حذف كلمة المرور بما انك قمت بتسجيل الدخول بواسطة OpenID" #~ msgid "delete my password" #~ msgstr "احذف كلمة المرور الخاصة بي" #~ msgid "Password Deleted" #~ msgstr "تم حذف كلمة المرور" ================================================ FILE: allauth/locale/az/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Jeyhun Piriyev , 2023 # msgid "" msgstr "" "Project-Id-Version: django-allauth\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-05-14 16:01+0000\n" "Last-Translator: Elchin Aliyev \n" "Language-Team: Azerbaijani \n" "Language: az\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.12-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Bu hesab hazırda aktiv deyil." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Siz əsas e-poçt ünvanınızı silə bilməzsiniz." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Bu e-poçt ünvanı artıq bu hesabla əlaqələndirilib." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Göstərdiyiniz e-poçt ünvanı və ya şifrə düzgün deyil." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Göstərdiyiniz istifadəçi adı və yaxud şifrə düzgün deyil." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "İstifadəçi artıq bu e-poçt ünvanı ilə qeydiyyatdan keçib." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Zəhmət olmasa cari şifrənizi yazın." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Yanlış kod." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Yanlış şifrə." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Etibarsız və ya müddəti bitmiş açar." #: account/adapter.py:79 msgid "Invalid login." msgstr "Etibarsız giriş." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Şifrə sıfırlama tokeni yanlışdır." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Siz %d-dən çox e-poçt ünvanı əlavə edə bilməzsiniz." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Bu telefon nömrəsi ilə artıq bir istifadəçi qeydiyyatdan keçib." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Həddindən artıq uğursuz giriş cəhdi. Biraz sonra yenidən cəhd edin." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Bu e-poçt ünvanı heç bir istifadəçi hesabına aid deyil." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Bu telefon nömrəsi heç bir istifadəçi hesabına aid deyil." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Əsas e-poçt ünvanınız təsdiqlənməlidir." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "" "İstifadəçi adı istifadə edilə bilməz. Zəhmət olmasa başqa istifadəçi adından " "istifadə edin." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Göstərdiyiniz istifadəçi adı və/yaxud şifrə düzgün deyil." #: account/adapter.py:98 msgid "Please select only one." msgstr "Zəhmət olmasa, yalnız birini seçin." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Yeni dəyər cari dəyərdən fərqli olmalıdır." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Səbirli olun, çox sayda sorğu göndərirsiniz." #: account/adapter.py:826 msgid "Use your password" msgstr "Şifrənizi istifadə edin" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Autentifikator tətbiqi və ya kod istifadə edin" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Təhlükəsizlik açarı istifadə edin" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} doğrulanmış kimi işarələndi." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "{email} doğrulanmış kimi işarələmək mümkün olmadı." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Seçilmiş e-poçt ünvanlarını doğrulanmış kimi işarələ" #: account/apps.py:11 msgid "Accounts" msgstr "Hesablar" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-poçt" #: account/fields.py:19 msgid "Email address" msgstr "E-poçt ünvanı" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" "Ölkə kodu daxil olmaqla telefon nömrəsini daxil edin (məsələn, ABŞ üçün +1)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefon" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Hər dəfə eyni şifrəni daxil etməlisiniz." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Şifrə" #: account/forms.py:67 msgid "Remember Me" msgstr "Məni xatırla" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "İstifadəçi adı" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Daxil ol" #: account/forms.py:115 msgid "Username, email or phone" msgstr "İstifadəçi adı, e-poçt və ya telefon" #: account/forms.py:117 msgid "Username or email" msgstr "İstifadəçi adı və ya e-poçt" #: account/forms.py:119 msgid "Username or phone" msgstr "İstifadəçi adı və ya telefon" #: account/forms.py:121 msgid "Email or phone" msgstr "E-poçt və ya telefon" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Şifrəni unutmusan?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-poçt (təkrar)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "E-poçt ünvanı təsdiqi" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-poçt (istəyə bağlı)" #: account/forms.py:314 msgid "Username (optional)" msgstr "İstifadəçi adı (istəyə bağlı)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Hər dəfə eyni e-poçtu daxil etməlisiniz." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Şifrə (təkrar)" #: account/forms.py:591 msgid "Current Password" msgstr "Mövcud şifrə" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Yeni şifrə" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Yeni şifrə (təkrar)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Kod" #: account/models.py:23 msgid "user" msgstr "istifadəçi" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "e-poçt ünvanı" #: account/models.py:31 msgid "verified" msgstr "doğrulanmış" #: account/models.py:32 msgid "primary" msgstr "əsas" #: account/models.py:38 msgid "email addresses" msgstr "e-poçt ünvanları" #: account/models.py:142 msgid "created" msgstr "yaradılmış" #: account/models.py:143 msgid "sent" msgstr "göndərildi" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "açar" #: account/models.py:149 msgid "email confirmation" msgstr "e-poçt təsdiqi" #: account/models.py:150 msgid "email confirmations" msgstr "e-poçt təsdiqləri" #: headless/apps.py:7 msgid "Headless" msgstr "Başsız" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "İstifadəçi ID-nizi görüntüləyin" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "E-poçt ünvanınıza baxın" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Əsas profil məlumatlarınıza baxın" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "İcazələri verin" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "'URI joker simvollarına icazə ver' aktivləşdirilmədikcə joker simvollara " "icazə verilmir." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' birdən çox joker simvol (*) ehtiva edir. Hər URI üçün yalnız bir " "joker simvola icazə verilir." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Joker simvollara yalnız URI-nin host hissəsində icazə verilir." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Avtorizasiya kodu" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Cihaz kodu" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Müştəri etimadnaməsi" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Yeniləmə tokeni" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Gizli" #: idp/oidc/models.py:44 msgid "Public" msgstr "Açıq" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Müştərinin tələb etməyə icazəsi olan əhatə dairəsi. Hər sətrdə bir dəyər " "daxil edin, məsələn: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Müştəri heç bir əhatə dairəsi göstərmədikdə, bu standart əhatə dairələri " "istifadə olunur. Hər sətrdə bir dəyər daxil edin, məsələn: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "İcazə verilən təqdimat növlərinin siyahısı. Hər sətrdə bir dəyər daxil edin, " "məsələn: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Çarpaz mənbə sorğuları üçün icazə verilən mənbələrin siyahısı, hər sətrdə " "bir ədəd." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Yönləndirmə URI-lərində və CORS mənbələrində joker simvollara (*) icazə " "verin. Aktivləşdirildikdə, URI-lər alt domenlərə uyğun gəlmək üçün tək ulduz " "ehtiva edə bilər." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "İcazə verilən cavab növlərinin siyahısı. Hər sətrdə bir dəyər daxil edin, " "məsələn: code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "müştəri" #: idp/oidc/models.py:116 msgid "clients" msgstr "müştərilər" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Siz iki faktorlu doğrulama ilə qorunan hesaba e-poçt ünvanı əlavə edə " "bilməzsiniz." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Siz iki faktorlu doğrulamanı deaktiv edə bilməzsiniz." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "İki faktorlu doğrulama aktivləşdirilmədən bərpa kodları yarada bilməzsiniz." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Siz e-poçt ünvanınızı doğrulamayana qədər iki faktorlu doğrulamanı " "aktivləşdirə bilməzsiniz." #: mfa/adapter.py:141 msgid "Master key" msgstr "Əsas açar" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Ehtiyat açar" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Açar nr. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Bərpa kodları" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP Autentifikator" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Doğrulama kodu" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Şifrəsiz" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Şifrəsiz əməliyyatın aktivləşdirilməsi yalnız bu açardan istifadə edərək " "daxil olmağa imkan verir, lakin biometrika və ya PIN qorunması kimi əlavə " "tələblər qoyur." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Bu e-poçt ünvanı ilə artıq hesab mövcuddur. Lütfən, əvvəlcə həmin hesaba " "daxil olun, sonra %s hesabınızı birləşdirin." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Yanlış token." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Hesabınızda şifrə quraşdırılmayıb." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Hesabınızın təsdiqlənmiş e-poçt ünvanı yoxdur." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Son qalan üçüncü tərəf hesabınızın əlaqəsini kəsə bilməzsiniz." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Üçüncü tərəf hesabı artıq başqa hesaba bağlıdır." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Sosial Hesablar" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "provayder" #: socialaccount/models.py:53 msgid "provider ID" msgstr "provayder ID" #: socialaccount/models.py:57 msgid "name" msgstr "ad" #: socialaccount/models.py:59 msgid "client id" msgstr "müştəri id" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "Tətbiq ID-si və ya istehlakçı açarı" #: socialaccount/models.py:64 msgid "secret key" msgstr "gizli açar" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API sirri, müştəri sirri və ya istehlakçı sirri" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Açar" #: socialaccount/models.py:82 msgid "social application" msgstr "sosial tətbiq" #: socialaccount/models.py:83 msgid "social applications" msgstr "sosial tətbiqlər" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "son giriş" #: socialaccount/models.py:121 msgid "date joined" msgstr "qoşulma tarixi" #: socialaccount/models.py:122 msgid "extra data" msgstr "əlavə məlumat" #: socialaccount/models.py:126 msgid "social account" msgstr "sosial hesab" #: socialaccount/models.py:127 msgid "social accounts" msgstr "sosial hesablar" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) və ya giriş tokeni (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "token sirri" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) və ya token yeniləyin (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "vaxtı bitir" #: socialaccount/models.py:175 msgid "social application token" msgstr "sosial tətbiq tokeni" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "sosial tətbiq tokenləri" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Yanlış profil məlumatı" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Daxil ol" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Ləğv et" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "\"%s\"-dən sorğu tokeni alınarkən yanlış cavab alındı. Cavab belə oldu: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "\"%s\"-dən giriş tokeni əldə edərkən yanlış cavab alındı." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "\"%s\" üçün heç bir sorğu tokeni saxlanılmadı." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "\"%s\" üçün heç bir giriş tokeni saxlanılmadı." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "\"%s\" ünvanında şəxsi resurslara giriş yoxdur." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "\"%s\"-dən sorğu tokeni alınarkən uğursuz cavab alındı." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Hesab Aktiv Deyil" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Bu hesab aktiv deyil." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "%(recipient)s ünvanına kod göndərdik. Kodun müddəti tezliklə bitir, ona görə " "zəhmət olmasa tezliklə daxil edin." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Təsdiq edin" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Yeni kod tələb et" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Girişi Təsdiqlə" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Hesabınızı qorumaq üçün lütfən, yenidən doğrulayın." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Alternativ variantlar" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "E-poçt Doğrulama" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "E-poçt Doğrulama Kodunu Daxil Edin" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Fərqli e-poçt ünvanı istifadə edin" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Daxil Ol" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Giriş Kodunu Daxil Edin" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Şifrə Sıfırlama" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Şifrə Sıfırlama Kodunu Daxil Edin" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Telefon Doğrulama" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Telefon Doğrulama Kodunu Daxil Edin" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Fərqli telefon nömrəsi istifadə edin" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "E-poçt Ünvanları" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Aşağıdakı e-poçt ünvanları hesabınızla əlaqələndirilir:" #: templates/account/email.html:25 msgid "Verified" msgstr "Doğrulanmış" #: templates/account/email.html:29 msgid "Unverified" msgstr "Doğrulanmamış" #: templates/account/email.html:34 msgid "Primary" msgstr "Əsas" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Əsas Et" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Doğrulamanı Yenidən Göndər" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Sil" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "E-poçt Ünvanı Əlavə Et" #: templates/account/email.html:70 msgid "Add Email" msgstr "E-poçt Əlavə Et" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Həqiqətən seçilmiş e-poçt ünvanını silmək istəyirsiniz?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Siz və ya başqası e-poçt ünvanından istifadə edərək\n" "hesab üçün qeydiyyatdan keçməyə çalışdığınız üçün bu e-məktubu alırsınız:\n" "\n" "%(email)s\n" "\n" "Lakin həmin e-poçt ünvanından istifadə edən hesab artıq mövcuddur. Bunu " "unutmusunuzsa, lütfən, hesabınızı bərpa etmək üçün unudulmuş şifrə " "prosedurundan istifadə edin:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Hesab Artıq Mövcuddur" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "%(site_name)s-dan Salam!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "%(site_name)s!\n" "%(site_domain)s istifadə etdiyiniz üçün təşəkkür edirik" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "Bu məktubu alırsınız, çünki hesabınızda aşağıdakı dəyişiklik edildi:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Bu dəyişikliyi tanımırsınızsa, dərhal lazımi təhlükəsizlik tədbirləri görün. " "Hesabınızdakı dəyişiklik aşağıdakı mənbədəndir:\n" "\n" "- IP ünvanı: %(ip)s\n" "- Brauzer: %(user_agent)s\n" "- Tarix: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "E-poçtunuz %(from_email)s-dən %(to_email)s-ə dəyişdirildi." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "E-poçt Dəyişdirildi" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "E-poçtunuz təsdiqləndi." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "E-poçt Təsdiqi" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "%(user_display)s istifadəçisi %(site_domain)s-də hesab qeydiyyatından keçmək " "üçün e-poçt ünvanınızı verdiyinə görə bu e-məktubu alırsınız." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "E-poçt doğrulama kodunuz aşağıda verilmişdir. Zəhmət olmasa onu açıq brauzer " "pəncərənizdə daxil edin." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Bunun düzgün olduğunu təsdiqləmək üçün %(activate_url)s ünvanına keçin" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Lütfən e-poçt ünvanınızı təsdiqləyin" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "%(deleted_email)s e-poçt ünvanı hesabınızdan silindi." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "E-poçt Silindi" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Giriş kodunuz aşağıda verilmişdir. Zəhmət olmasa onu açıq brauzer " "pəncərənizdə daxil edin." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Bu hərəkəti siz başlatmamısınızsa, bu məktubu etinasız qoya bilərsiniz." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Giriş Kodu" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Şifrəniz dəyişdirildi." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Şifrə Dəyişdirildi" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Şifrə sıfırlama kodunuz aşağıda verilmişdir. Zəhmət olmasa onu açıq brauzer " "pəncərənizdə daxil edin." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Şifrə Sıfırlama Kodu" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Siz və ya başqa biri hesabınız üçün şifrə sıfırlamasını tələb etdiyinə görə " "bu e-məktubu alırsınız.\n" "Əgər belə bir sorğu etməmisinizsə, bu e-məktubu gözardı edə bilərsiniz. " "Şifrənizi sıfırlamaq üçün aşağıdakı linkə keçid edin." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Əgər unutmusunuzsa, istifadəçi adınız %(username)s-dir." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Şifrə Sıfırlama E-poçtu" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Şifrəniz sıfırlandı." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Şifrəniz təyin edildi." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Şifrə Təyin Edildi" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Bu e-məktubu alırsınız, çünki siz və ya başqa biri %(email)s e-poçt ünvanı " "ilə hesaba daxil olmağa çalışıb. Lakin verilənlər bazamızda belə bir hesab " "qeydimiz yoxdur." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Bu siz idinizsə, aşağıdakı linkdən istifadə edərək hesab yarada bilərsiniz." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Naməlum Hesab" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "E-poçt Ünvanı" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Mövcud e-poçt" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Dəyişdirilir" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "E-poçt ünvanınız hələ də doğrulamanı gözləyir." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Dəyişikliyi Ləğv Et" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Dəyiş" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "E-poçt Dəyiş" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "E-poçt Ünvanını Təsdiqlə" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Lütfən, təsdiq edin ki, %(email)s " "%(user_display)s istifadəçisi üçün e-poçt ünvanıdır." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "%(email)s-u təsdiqləmək mümkün deyil, çünki, o, artıq başqa hesab tərəfindən " "təsdiqlənib." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Bu e-poçt təsdiqi linkinin vaxtı keçib və ya etibarsızdır. Lütfən, yeni e-poçt təsdiq sorğusu göndərin." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Hələ hesab yaratmamısınızsa, lütfən, əvvəlcə %(link)sqeydiyyatdan " "keçin%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Giriş açarı ilə daxil olun" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Mənə giriş kodu göndərin" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Çıxış" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Hesabdan çıxmaq istədiyinizə əminsiniz?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Siz əsas e-poçt ünvanınızı (%(email)s) silə bilməzsiniz." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Təsdiq e-məktubu %(email)s ünvanına göndərildi." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Siz %(email)s-u təsdiqlədiniz." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "%(email)s e-poçt ünvanı silindi." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "%(name)s olaraq uğurla daxil oldun." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Siz hesabdan çıxdınız." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "%(recipient)s ünvanına giriş kodu göndərildi." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Şifrə uğurla dəyişdirildi." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Şifrə uğurla təyin edildi." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "%(phone)s nömrəsinə doğrulama kodu göndərildi." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "%(phone)s telefon nömrəsini doğruladınız." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Əsas e-poçt ünvanı təyin edildi." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Şifrəni Dəyiş" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Şifrəni Unutmusunuz?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Şifrənizi unutmusunuz? Aşağıya e-poçt ünvanınızı daxil edin və biz sizə onu " "sıfırlamağa imkan verən e-məktub göndərəcəyik." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Şifrəmi Sıfırlayın" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Şifrənizi sıfırlamaqla bağlı hər hansı probleminiz olarsa, bizimlə əlaqə " "saxlayın." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Sizə e-məktub göndərdik. Əgər onu almamısınızsa, spam qovluğunuzu yoxlayın. " "Əks halda, bir neçə dəqiqə ərzində onu almasanız, bizimlə əlaqə saxlayın." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Yanlış Token" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Şifrə sıfırlama linki etibarsız idi, ola bilsin ki, artıq istifadə olunub. " "Lütfən, yeni şifrə sıfırlamasını tələb " "edin." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Şifrəniz dəyiştirildi." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Şifrə Təyin Et" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Telefonu Dəyiş" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Mövcud telefon" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Telefon nömrəniz hələ də doğrulamanı gözləyir." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Telefonu Dəyiş" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Şifrənizi daxil edin:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Şifrəsiz giriş üçün xüsusi kod alacaqsınız." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Kod Tələb Et" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Digər giriş seçimləri" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Qeydiyyatdan keçin" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Qeydiyyatdan Keç" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Artıq bir hesabınız var? Lütfən %(link)sdaxil olun%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Giriş açarı ilə qeydiyyatdan keçin" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Giriş Açarı ilə Qeydiyyat" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Digər seçimlər" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Qeydiyyat Bağlıdır" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Üzr istəyirik, lakin qeydiyyat hazırda bağlıdır." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Qeyd" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Siz artıq %(user_display)s kimi daxil olmusunuz." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Xəbərdarlıq:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Hazırda ayarlanmış e-poçt ünvanınız yoxdur. Siz mütləq e-poçt ünvanı əlavə " "etməlisiniz ki, bildirişlər ala, şifrənizi sıfırlayasınız və s." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "E-poçt Ünvanınızı Doğrulayın" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Doğrulama üçün sizə e-məktub göndərdik. Qeydiyyat prosesini yekunlaşdırmaq " "üçün verilən linki izləyin. Doğrulama e-məktubunu əsas gələnlər qutunuzda " "görmürsünüzsə, spam qovluğunuzu yoxlayın. Bir neçə dəqiqə ərzində doğrulama " "e-məktubunu almasanız, bizimlə əlaqə saxlayın." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Saytın bu hissəsi siz olduğunuzu təsdiq etməyimizi tələb edir\n" ".Bu məqsədlə sizdən e-poçt ünvanınızın sahibliyini\n" "doğrulamanızı tələb edirik." #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Doğrulama üçün\n" "sizə e-məktub göndərdik. Zəhmət olmasa həmin e-poçtun içindəki linkə keçid " "edin. Doğrulama e-poçtunu əsas gələnlər qutunuzda görmürsünüzsə, spam " "qovluğunuzu yoxlayın. Əks halda\n" "bir neçə dəqiqə ərzində onu almasanız, bizimlə əlaqə saxlayın." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Qeyd: siz hələ də e-poçt " "ünvanınızı dəyişə bilərsiniz." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Mesajlar:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menyu:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Hesab Əlaqələri" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "İki Faktorlu Doğrulama" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sessiyalar" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "İcazə ver" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s %(site_name)s hesabınıza daxil olmaq istəyir." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Cihaz Kodunu Daxil Edin" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Cihazınızda göstərilən kodu daxil edin." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Davam et" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Cihazı Təsdiq Et" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Bu cihazı avtorizasiya etmək üçün %(client_name)s-da göstərilən kodu təsdiq " "edin." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Rədd et" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Cihaz Avtorizasiya Edildi" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "%(client_name)s cihazınızı uğurla avtorizasiya etdiniz." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Cihaz Rədd Edildi" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "%(client_name)s cihazınız üçün avtorizasiya rədd edildi." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Xəta" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Daxil Olmuş Qal" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Hesabınız iki faktorlu doğrulama ilə qorunur. Zəhmət olmasa autentifikator " "kodunu daxil edin:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "Yeni iki faktorlu doğrulama bərpa kodları dəsti yaradıldı." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Yeni Bərpa Kodları Yaradıldı" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Autentifikator tətbiqi aktivləşdirildi." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Autentifikator Tətbiqi Aktivləşdirildi" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Autentifikator tətbiqi deaktiv edildi." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Autentifikator Tətbiqi Deaktiv Edildi" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Yeni təhlükəsizlik açarı əlavə edildi." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Təhlükəsizlik Açarı Əlavə Edildi" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Təhlükəsizlik açarı silindi." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Təhlükəsizlik Açarı Silindi" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Autentifikator Tətbiqi" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Autentifikator tətbiqindən istifadə edərək doğrulama aktivdir." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Autentifikator tətbiqi aktiv deyil." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Deaktiv et" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Aktivləşdir" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Təhlükəsizlik Açarları" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "%(count)s təhlükəsizlik açarı əlavə etdiniz." msgstr[1] "%(count)s təhlükəsizlik açarı əlavə etdiniz." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Heç bir təhlükəsizlik açarı əlavə edilməyib." #: templates/mfa/index.html:62 msgid "Manage" msgstr "İdarə et" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Əlavə et" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Bərpa kodları" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "%(total_count)s bərpa kodundan %(unused_count)s-i mövcuddur." msgstr[1] "%(total_count)s bərpa kodundan %(unused_count)s-i mövcuddur." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Bərpa kodları qurulmayıb." #: templates/mfa/index.html:96 msgid "View" msgstr "Bax" #: templates/mfa/index.html:102 msgid "Download" msgstr "Yüklə" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Yarat" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Yeni bərpa kodları dəsti yaradıldı." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Təhlükəsizlik açarı əlavə edildi." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Təhlükəsizlik açarı silindi." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Autentifikator kodunu daxil edin:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Hesabınız üçün yeni bərpa kodları dəsti yaratmaq üzrəsiniz." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Bu əməliyyat mövcud kodlarınızı etibarsız edəcək." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Siz əminsiniz?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "İstifadə edilməmiş kodlar" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Kodları yükləyin" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Yeni kodlar yarat" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Autentifikator Tətbiqini Aktivləşdirin" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Hesabınızı iki faktorlu doğrulama ilə qorumaq üçün autentifikator " "tətbiqinizlə aşağıdakı QR kodunu skan edin. Sonra, aşağıdakı proqram " "tərəfindən yaradılan doğrulama kodunu daxil edin." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Autentifikator sirri" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Siz bu sirri saxlaya və ondan autentifikator tətbiqinizi daha sonra yenidən " "quraşdırmaq üçün istifadə edə bilərsiniz." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Autentifikator Tətbiqini Deaktiv Et" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Siz autentifikator tətbiqinə əsaslanan doğrulamanı deaktiv etmək üzrəsiniz. " "Siz əminsiniz?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Bu Brauzerə Güvənirsiniz?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Bu brauzerə güvənməyi seçsəniz, növbəti dəfə daxil olarkən sizdən doğrulama " "kodu istənilməyəcək." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "%(period)s müddətinə güvən" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Güvənmə" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Təhlükəsizlik Açarı Əlavə Et" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Təhlükəsizlik Açarını Sil" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Bu təhlükəsizlik açarını silmək istədiyinizə əminsiniz?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "İstifadə" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Giriş açarı" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Təhlükəsizlik açarı" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Bu açar giriş açarı olub-olmadığını göstərmir." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Müəyyən edilməyib" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "%(created_at)s tarixində əlavə edildi" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Son istifadə %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Redaktə et" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Təhlükəsizlik Açarını Redaktə Et" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Saxla" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Giriş Açarı Yarat" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Hesabınız üçün giriş açarı yaratmaq üzrəsiniz. Daha sonra əlavə açarlar " "əlavə edə biləcəyiniz üçün açarları fərqləndirmək üçün təsviri ad istifadə " "edə bilərsiniz." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Yarat" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Bu funksionallıq JavaScript tələb edir." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Üçüncü Tərəf Giriş Xətası" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Üçüncü tərəf hesabınız vasitəsilə daxil olmağa cəhd edərkən xəta baş verdi." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Aşağıdakı üçüncü tərəf hesablarından hər hansı birini istifadə edərək " "hesabınıza daxil ola bilərsiniz:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Hazırda bu hesaba bağlı heç bir üçüncü tərəf hesabınız yoxdur." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Üçüncü Tərəf Hesabı Əlavə Et" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "%(provider)s-dən üçüncü tərəf hesabı hesabınıza bağlandı." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Üçüncü Tərəf Hesabı Bağlandı" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "%(provider)s-dən üçüncü tərəf hesabının hesabınızla əlaqəsi kəsildi." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Üçüncü Tərəf Hesabının Əlaqəsi Kəsildi" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "%(provider)s ilə əlaqə" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Siz %(provider)s-dən yeni üçüncü tərəf hesabını qoşmaq üzrəsiniz." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "%(provider)s vasitəsi ilə daxil olun" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" "Siz %(provider)s-dən üçüncü tərəf hesabından istifadə etməklə daxil olmaq " "üzrəsiniz." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Giriş Ləğv Edildi" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Mövcud hesablarınızdan birini istifadə edərək saytımıza daxil olmağı ləğv " "etmək qərarına gəldiniz. Bu səhvdirsə, daxil olun." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Üçüncü tərəf hesabı bağlandı." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Üçüncü tərəf hesabının əlaqəsi kəsildi." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Siz \n" " %(site_name)s hesabına daxil olmaq üçün %(provider_name)s hesabınızdan " "istifadə etmək üzrəsiniz. Son addım olaraq, aşağıdakı formu doldurun:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Və ya üçüncü tərəf istifadə edin" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Bütün digər sessiyalardan çıxıldı." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Başlama Vaxtı" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP Ünvanı" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Brauzer" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Son görülmə" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Mövcud" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Digər Sessiyalardan Çıx" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "İstifadəçi Sessiyaları" #: usersessions/models.py:94 msgid "session key" msgstr "sessiya açarı" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Hesab Əlaqələri" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Şifrə ən az {0} simvoldan ibarət olmalıdır." #, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Sizin və ya başqa birinin istifadəçi hesabınız üçün\n" #~ "şifrə tələb etdiyini bildirmək üçün bu e-məktubu alırsınız. Lakin, bizim " #~ "verilənlər bazamızda\n" #~ "%(email)s e-poçt ünvanına sahib heç bir istifadəçi məlumatı yoxdur.\n" #~ "\n" #~ "Şifrənin sıfırlanmasını tələb etməmisinizsə, bu e-məktubu gözardı edə " #~ "bilərsiniz.\n" #~ "\n" #~ "Əgər bu siz idinizsə, siz aşağıdakı linkdən istifadə edərək hesaba daxil " #~ "ola bilərsiniz." #~ msgid "The following email address is associated with your account:" #~ msgstr "Aşağıdakı e-poçt ünvanı hesabınızla əlaqələndirilib:" #~ msgid "Change Email Address" #~ msgstr "E-poçt Ünvanını Dəyiş" ================================================ FILE: allauth/locale/bg/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-07-25 20:23+0200\n" "Last-Translator: Ilia Iliev \n" "Language-Team: Bulgarian \n" "Language: bg\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.12-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Този акаунт в момента е неактивен." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Не можете да премахнете основния си имейл адрес." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Този имейл адрес вече е свързан с този акаунт." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "E-mail адресът и/или паролата, които въведохте, са грешни." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Телефонният номер и/или паролата, които въведохте, са грешни." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Вече има регистриран потребител с този имейл адрес." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Моля, въведете вашата текуща парола." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Неправилен код." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Грешна парола." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Грешен или изтекъл ключ." #: account/adapter.py:79 msgid "Invalid login." msgstr "Невалиден вход." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Невалиден код за възстановяване на парола." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Не можете да добавяте повече от %d e-mail адреса." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Вече има регистриран потребител с този телефонен номер." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Твърде много неуспешни опити за влизане. Опитайте отново по-късно." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Няма потребител с този e-mail адрес." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Няма потребител с този телефонен номер." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Основният ви e-mail адрес трябва да бъде потвърден." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "" "Това потребителско име не може да бъде използвано. Моля, изберете друго." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Потребителското име и/или паролата, които въведохте, са грешни." #: account/adapter.py:98 msgid "Please select only one." msgstr "Моля изберете само една опция." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Новата стойност трябва да е различна от настоящата." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Бъдете търпеливи, изпращате твърде много трафик." #: account/adapter.py:826 msgid "Use your password" msgstr "Използвайте парола" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Използвайте апликация за автентикация или код" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Използвайте код за сигурност" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "Имейл адрес {email} е маркиран като потвърден." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Неуспешно маркиране на {email} като потвърден." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Изберете имейл адресите като верифицирани" #: account/apps.py:11 msgid "Accounts" msgstr "Акаунти" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-mail" #: account/fields.py:19 msgid "Email address" msgstr "E-mail адрес" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Въведете телефонен номер с код на държавата (+359 за България)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Телефон" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Трябва да въведете една и съща парола." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Парола" #: account/forms.py:67 msgid "Remember Me" msgstr "Запомни ме" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Потребителско име" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Акаунт" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Потребителско име, e-mail или телефонен номер" #: account/forms.py:117 msgid "Username or email" msgstr "Потребителско име или e-mail" #: account/forms.py:119 msgid "Username or phone" msgstr "Потребителско име или телефон" #: account/forms.py:121 msgid "Email or phone" msgstr "E-mail или телефонен номер" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Забрави си паролата?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-mail (отново)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Потвърждение на e-mail адрес" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-mail (опционален)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Потребителско име(по избор)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Трябва да въведете един и същ email." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Парола (отново)" #: account/forms.py:591 msgid "Current Password" msgstr "Текуща парола" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Нова парола" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Нова парола (отново)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Код" #: account/models.py:23 msgid "user" msgstr "потребител" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "e-mail адрес" #: account/models.py:31 msgid "verified" msgstr "потвърден" #: account/models.py:32 msgid "primary" msgstr "основен" #: account/models.py:38 msgid "email addresses" msgstr "email адреси" #: account/models.py:142 msgid "created" msgstr "създадено" #: account/models.py:143 msgid "sent" msgstr "изпратено" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "ключ" #: account/models.py:149 msgid "email confirmation" msgstr "email потвърждение" #: account/models.py:150 msgid "email confirmations" msgstr "email потвърждения" #: headless/apps.py:7 msgid "Headless" msgstr "Скрит режим" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Преглед на вашия потребителски идентификатор" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Преглед на вашия имейл адрес" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Преглед на основната информация за вашия профил" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Предоставяне на разрешения" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Заместващи символи (*) не са разрешени, освен ако не е включена опцията " "'Разрешаване на заместващи символи в URI'." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' съдържа повече от един заместващ символ (*). Разрешен е само един " "заместващ символ за URI." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "" "Заместващи символи (*) са разрешени само в частта за име на хост от URI." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Код за оторизация" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Код на устройство" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Клиентски идентификационни данни" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Токен за обновяване" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Поверителен" #: idp/oidc/models.py:44 msgid "Public" msgstr "Публичен" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Обхват(ите), които клиентът има право да заявява. Въведете по една стойност " "на ред, напр.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "В случай че клиентът не посочи обхват, се използват тези по подразбиране. " "Въведете по една стойност на ред, напр.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Списък с разрешени типове разрешения. Въведете по една стойност на ред, " "напр.: authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "Списък с разрешени източници за cross-origin заявки, по един на ред." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Разрешаване на заместващи символи (*) в URI за пренасочване и CORS " "източници. Когато е включено, URI адресите могат да съдържат една звездичка " "за съвпадение на поддомейни." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Списък с разрешени типове отговори. Въведете по една стойност на ред, напр.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "клиент" #: idp/oidc/models.py:116 msgid "clients" msgstr "клиенти" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Не можете да добавите имейл адрес към акаунт, който ползва двустепенна " "защита." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Не можете да деактивирате двустепенна защита." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Не можете да генерирате кодове за възстановяване, без да сте включили " "двустепенна защита." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Не можете да активирате двустепенна защита, преди да сте верифицирали имейл " "адреса си." #: mfa/adapter.py:141 msgid "Master key" msgstr "Основен ключ" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Резервен ключ" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Ключ №{number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Кодове за възстановяване" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP Автентикатор" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Автентикиращ код" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Без парола" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Включвайки режим без парола Ви позволява да се вписвате чрез ключ, но има " "допълнителни изисквания например биометрични данни или защита с ПИН код." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Вече съществува акаунт с този имейл адрес. Моля, първо влезте в този акаунт " "и тогава свържете вашия %s акаунт." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Грешен код." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Вашият акаунт няма парола." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Вашият акаунт няма потвърден e-mail адрес." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Не можете да прекъснете последната си връзка към външен акаунт." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Този акаунт вече е свързан с друг акаунт." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Социални акаунти" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "доставчик" #: socialaccount/models.py:53 msgid "provider ID" msgstr "Идентификатор на доставчик" #: socialaccount/models.py:57 msgid "name" msgstr "име" #: socialaccount/models.py:59 msgid "client id" msgstr "id на клиент" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "ID на приложение, или ключ на консуматор" #: socialaccount/models.py:64 msgid "secret key" msgstr "таен ключ" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "таен ключ на API, клиент или консуматор" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Ключ" #: socialaccount/models.py:82 msgid "social application" msgstr "социално приложение" #: socialaccount/models.py:83 msgid "social applications" msgstr "социални приложения" #: socialaccount/models.py:118 msgid "uid" msgstr "УИД" #: socialaccount/models.py:120 msgid "last login" msgstr "последно влизане" #: socialaccount/models.py:121 msgid "date joined" msgstr "дата на регистрация" #: socialaccount/models.py:122 msgid "extra data" msgstr "допълнителни данни" #: socialaccount/models.py:126 msgid "social account" msgstr "социален акаунт" #: socialaccount/models.py:127 msgid "social accounts" msgstr "социални акаунти" #: socialaccount/models.py:161 msgid "token" msgstr "код" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) или access token (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "таен код" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) или refresh token (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "изтича" #: socialaccount/models.py:175 msgid "social application token" msgstr "код на социално приложение" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "кодове на социални приложения" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Невалидни профилни данни" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Влизане" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Откажи" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Грешен отговор при получаване на код за заявка от \"%s\". Отговорът беше: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Грешен отговор при получаване на код за достъп от \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Няма запазен код за заявка за \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Няма запазен код за достъп за \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Няма достъп до лични данни при \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Грешен отговор при получаване на код за заявка от \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Неактивен акаунт" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Този акаунт е неактивен." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Изпратихме Ви код за потвърждение на %(recipient)s. Кода изтича скоро, моля " "въведете го скоро." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Потвърди" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Поискай нов ключ" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Потвърждение на достъпа" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Моля, влезте отново, за да потвърдим сигурността на акаунта Ви." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Алтернативи" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Имейл за потвърждение" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Въведете код за потвърждение от имейла" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Използвайте друг e-mail адрес" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Вход" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Въведете код за влизане" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Възстановяване на парола" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Въведете код за възстановяване на паролата" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Потвърждение с телефонен номер" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Въведете код за потвърждение от телефона" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Използвайте друг телефонен номер" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "E-mail адреси" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Следните e-mail адреси са свързани с вашия акаунт:" #: templates/account/email.html:25 msgid "Verified" msgstr "Потвърден" #: templates/account/email.html:29 msgid "Unverified" msgstr "Непотвърден" #: templates/account/email.html:34 msgid "Primary" msgstr "Основен" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Направи основен" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Изпрати потвърждение отново" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Премахни" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Добяне на е-mail адрес" #: templates/account/email.html:70 msgid "Add Email" msgstr "Добави e-mail" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Наистина ли искате да премахнете избрания e-mail адрес?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Получавате този имейл, защото Вие или някой друг се опитва да влезе в \n" "акаунт използващ този имейл:\n" "\n" "%(email)s\n" "\n" "Въпреки това, акаунт с този имейл адрес вече съществува. Ако сте\n" "забравили за този акаунт, моля използвайте процедурата за възстановяване\n" "на паролата, за да възстановите достъпа до него:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Акаунтът вече съществува" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Здравейте от %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Благодарим, че ползвате %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Получавате този имейл, защото бяха направени следните промени по Вашият " "акаунт:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Ако не разпознавате тези промени, моля вземете мерки за сигурността веднага. " "Първоизточникът на промените е от:\n" "\n" "- IP адрес: %(ip)s\n" "- Браузър: %(user_agent)s\n" "- Дата: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Имейлът Ви беше променен от %(from_email)s на %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Имейлът е променен" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Имейлът Ви беше потвърден." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Имейл потвърждение" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Получавате този имейл, защото потребител %(user_display)s е дал Вашият имейл " "адрес, за да регистрира акаунт в %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Можете да откриете Вашият код за верификация по-долу. Моля, въведете го в " "отворения прозорец на браузъра." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "За да потвърдите, моля посетете %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Моля, потвърдете вашия e-mail адрес" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "Имейлът %(deleted_email)s е премахнат от Вашият акаунт." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Премахнат имейл" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Вашият код за влизане е посочен по-долу. Моля, въведете го в отворения " "прозорец на браузъра." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Този имейл може да бъде игнориран безопасно, ако не сте инициирали това " "действие." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Код за влизане" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Паролата Ви е променена." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Паролата е сменена" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Вашият код за смяна на паролата е посочен по-долу. Моля, въведете го в " "отворения прозорец на браузъра." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Код за възстановяване на паролата" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Получавате този имейл, защото Вие или някой друг е поискал смяна на парола " "за Вашия потребителски акаунт.\n" "Можете да го пренебрегнете, ако не сте поискали възстановяване на парола. " "Кликнете линка по-долу за да възстановите Вашата парола." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "В случай, че сте забравили, вашето потребителско име е %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Възстановяване на парола" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Паролата Ви е сменена." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Паролата Ви е сменена." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Зададена парола" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Получавате този имейл, защото Вие или някой друг се опита да влезе в акаунт " "с имейл адрес %(email)s. Въпреки това, нямаме запис за такъв акаунт в нашата " "база данни." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "Ако сте Вие, можете да се впишете за акаунт използвайки линка по-долу." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Неразпознат акаунт" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Имейл адрес" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Текущ имейл" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Промяна към" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Вашият имейл адрес все още очаква потвърждение." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Прекъсване на промяна" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Промяна към" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Промяна на имейл" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Потвърждение на e-mail адрес" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Моля, потвърдете, че %(email)s е имейл " "адрес на потребител %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Не може да се потвърди %(email)s, защото вече е потвърден от различен акаунт." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Този линк за потвърждение на имейла е изтекъл или невалиден. Моля, подайте нова заявка за потвърждение на e-mail." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Ако все още не сте създали акаунт, моля, %(link)sрегистрирайте " "се%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Влизане с код за достъп" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Изпрати ми ключ за влизане" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Изход" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Сигурни ли сте, че искате да излезете?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Не можете да премахнете основния си e-mail адрес (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Потвърждение на e-mail адрес изпратено на %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Потвърдихте e-mail адрес %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Премахнат e-mail адрес %(email)s." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Успешно влязохте като %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Излязохте." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Ключ за влизане беше изпратен на %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Паролата беше сменена успешно." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Паролата беше запазена успешно." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Ключ за верификация беше изпратен на %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Потвърдили сте телефонен номер %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Основният e-mail адрес беше избран." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Смяна на парола" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Забравена парола?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Забравили сте паролата си? Въведете вашия имейл адрес по-долу и ще ви " "изпратим имейл с инструкции за нулиране на паролата." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Възстанови паролата ми" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Моля, свържете се с нас, ако имате проблеми с възстановяването на вашата " "парола." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Изпратихме Ви имейл. Ако не сте го получили, моля, проверете папката със " "спам. Ако все пак не го получите в рамките на няколко минути, моля, свържете " "се с нас." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Грешен код" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Линкът за възстановяване на парола е невалиден, може би защото вече е бил " "използван. Моля, заявете ново " "възстановяване на парола." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Паролата ви е сменена." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Създаване на парола" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Промяна на телефон" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Текущ телефонен номер" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Вашият телефонен номер все още очаква потвърждение." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Промяна на телефон" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Напишете паролата си:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Ще получите специален код за влизане без парола." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Поискай ключ" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Други опции за влизане" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Регистрация" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Регистрация" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Вече имате акаунт? Тогава, моля, %(link)sвлезте%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Регистрация с ключ за достъп" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Регистрация с ключ за достъп" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Други опции" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Регистрацията е затворена" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Съжаляваме, но в момента регистрацията е затворена." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Забележка" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "вече сте влезли като %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Предупреждение:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "В момента нямате добавен имейл адрес. Трябва да добавите имейл адрес, за да " "можете да получавате известия, да нулирате паролата си и др." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Потвърдете вашия e-mail адрес" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Изпратихме ви имейл за потвърждение. Следвайте връзката в него, за да " "финализирате процеса на регистрация. Ако не виждате имейла за потвърждение в " "основната си поща, проверете папката със спам. Моля, свържете се с нас, ако " "не получите имейла за потвърждение в рамките на няколко минути." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Тази част от сайта изисква да потвърдим, че\n" "Вие сте този, за когото се представяте. За тази цел е необходимо да\n" "потвърдите собствеността на Вашия имейл адрес. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Изпратихме ви имейл за\n" "потвърждение. Моля, кликнете върху връзката в този имейл. Ако не виждате " "имейла за потвърждение в основната си поща, проверете папката със спам. Ако " "не го получите\n" "в рамките на няколко минути, моля, свържете се с нас." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Забележка: можете да смените " "вашия имейл адрес." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Съобщения:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Меню:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Свързани акаунти" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Двустепенна автентикация" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Сесии" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Оторизация" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s иска достъп до вашия %(site_name)s акаунт." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Въведете код на устройство" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Въведете кода, показан на вашето устройство." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Продължи" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Потвърждение на устройство" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Моля, потвърдете кода, показан на вашия %(client_name)s, за да оторизирате " "това устройство." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Откажи" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Устройството е оторизирано" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Успешно оторизирахте вашето %(client_name)s устройство." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Устройството е отказано" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Оторизацията за вашето %(client_name)s устройство беше отказана." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Грешка" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Остани вписан" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Вашият акаунт е защитен с двустепенна автентикация. Моля, въведете Вашият " "ключ за автентикация:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Нов сет от ключове за възстановяване на двустепенната автентикация са " "генерирани." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Нови ключове за възстановяване са генерирани" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Апликацията за автентикация е активирана." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Автентикиращата апликация е активирана" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Автентикиращата апликация е деактивирана." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Автентикиращата апликация е деактивирана" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Нов ключ за сигурност беше добавен." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Добавен е ключ за сигурност" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Беше премахнат ключ за сигурност." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Премахнат е ключ за сигуреност" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Автентикираща апликация" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Автентикация чрез автентикираща апликация е активирана." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Автентикиращата апликация е неактивна." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Деактивирай" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Активирай" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Ключове за сигурност" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Добавихте %(count)s ключ за сигурност." msgstr[1] "Добавихте %(count)s ключа за сигурност." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Не бяха добавени ключове за сигурност." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Управление" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Добави" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Ключове за възстановяване" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Има %(unused_count)s налични от %(total_count)s ключове за възстановяване." msgstr[1] "" "Има %(unused_count)s налични от %(total_count)s ключове за възстановяване." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Няма създадени ключове за възстановяване." #: templates/mfa/index.html:96 msgid "View" msgstr "Изглед" #: templates/mfa/index.html:102 msgid "Download" msgstr "Свали" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Генерирай" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Нов сет от ключове за възстановяване бяха генерирани." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Добавен е ключ за възстановяване." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Премахнат е ключ за възстановяване." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Въведете ключ за автентикация:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "" "На път сте да генерирате нов сет с ключове за възстановяване за Вашият " "акаунт." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Това действие ще направи съществуващите ключове невалидни." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Сигурни ли сте?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Неизползвани ключове" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Свалете ключовете" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Генерирайте нови ключове" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Активация на Автентикираща апликация" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "За да защитите акаунта си с двустепенна автентикация, сканирайте QR кода по-" "долу с Вашата автентикираща апликация. След това, въведете верифициращия код " "от апликацията в полето по-долу." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Таен код за автентикираща апликация" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Можете да запазите този таен ключ и да го използвате ако някога " "преинсталирате автентикиращата апликация." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Деактивирайте автентикиращата апликация" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "На път сте да деактивирате апликация за автентикация. Сигурни ли сте?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Вярвате ли на този браузър?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Ако решите да се доверите на този браузър, няма да бъдете помолени за код за " "потвърждение следващия път, когато влезете." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Доверие за %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Не се доверявайте" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Добавяне на ключ за сигурност" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Премахване на ключ за сигурност" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Сигурни ли сте, че искате да премахнете ключа за сигурност?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Употреба" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Ключ за достъп" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Ключ за сигурност" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Ключът не индикира дали е ключ за достъп или не." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Неупоменат" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Добавено на %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Последно използван на %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Редактиране" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Редактирай ключ за сигурност" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Запази" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Създай ключ за достъп" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Предстои да създадете ключ за достъп за вашия акаунт. Тъй като по-късно " "можете да добавяте допълнителни ключове, можете да използвате описателно " "име, за да ги разграничавате." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Създай" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Тази функционалност изисква JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Неуспешно влизане чрез приложение за автентикация на трета страна" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "Възникна грешка при опит за влизане чрез вашия акаунт на трета страна." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Можете да влезете в акаунта си, използвайки някой от следните акаунти на " "трети страни:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "В момента нямате свързани акаунти на трети страни с този акаунт." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Добавяне на акаунт на трета страна" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Акаунт от %(provider)s беше свързан с вашия акаунт." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Добавен е акаунт на трета страна" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "Акаунт от %(provider)s беше прекъснат от вашия акаунт." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Връзката с акаунт на трета страна е прекъсната" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Свържи с %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Предстои да свържете нов акаунт на трета страна от %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Влизане с %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "Предстои да влезете с акаунт на %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Вход прекъснат" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Прекъснахте влизането в нашия сайт чрез ваш съществуващ акаунт. Ако това е " "било грешка, моля, влезте." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Акаунт на трета страна беше свързан с Вашият." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Връзката с акаунт на трета страна беше прекъсната." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "На път сте да използвате вашия %(provider_name)s акаунт за вход в\n" "%(site_name)s. Като последна стъпка, моля, попълнете следната форма:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Или използвайте трета страна" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Излезнахте от всички останали сесии." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Започнато на" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP Адрес" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Браузър" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Последно видян на" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Текуща" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Отпиши други сесии" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Потребителски сесии" #: usersessions/models.py:94 msgid "session key" msgstr "сесиен ключ" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Свързани акаунти" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Паролата трябва да бъде поне {0} символа." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Получавате този e-mail, защото вие или някой друг е поискал парола за " #~ "вашия потребителски акаунт.\n" #~ "На сървъра обаче не беше намерен потребител свързван с електронния адрес " #~ "%(email)s.\n" #~ "\n" #~ "Можете да пренебрегнете това писмо, ако не сте поискали възстановяване на " #~ "парола. Кликнете линка по-долу, за да направите нов акаунт." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "Следните e-mail адреси са свързани с вашия акаунт:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Потвърждение на e-mail адрес" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Моля, влезте с някой\n" #~ "от съществуващите ви външни акаунти. Или %(link)sсе регистрирайте\n" #~ "за %(site_name)s акаунт и влезте по-долу:" #~ msgid "or" #~ msgstr "или" #~ msgid "change password" #~ msgstr "смени паролата" #~ msgid "OpenID Sign In" #~ msgstr "Вход с OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "Този e-mail адрес вече е свързан с друг акаунт." ================================================ FILE: allauth/locale/ca/LC_MESSAGES/django.po ================================================ # DJANGO-ALLAUTH. # Copyright (C) 2016 # This file is distributed under the same license as the django-allauth package. # # Translators: # Marc Seguí Coll , 2022. # msgid "" msgstr "" "Project-Id-Version: django-allauth\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2024-09-26 16:15+0000\n" "Last-Translator: Ajordat \n" "Language-Team: Catalan \n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.8-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Ara mateix aquest compte està inactiu." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "No podeu eliminar el vostre correu electrònic principal." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Aquest correu electrònic ja està associat amb aquest compte." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "" "El correu electrònic i/o la contrasenya que heu especificat no són correctes." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "" "El número de telèfon i/o la contrasenya que heu especificat no són correctes." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "" "Un usuari ja ha estat registrat amb aquesta direcció de correu electrònic." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Si us plau, escriviu la vostra contrasenya actual." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Codi incorrecte." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Contrasenya actual." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Clau no vàlida o caducada." #: account/adapter.py:79 msgid "Invalid login." msgstr "Inici de sessió no vàlid." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "El token per reiniciar la contrasenya no és vàlid." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "No es poden afegit més de %d adreces de correu electrònic." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Un usuari ja ha estat registrat amb aquest número de telèfon." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Massa intents fallits. Intenteu-ho de nou més tard." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "El correu electrònic no està assignat a cap compte d'usuari." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "El número de telèfon no està assignat a cap compte d'usuari." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "La vostra adreça de correu electrònic principal ha de ser verificada." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "" "Aquest nom d'usuari no pot ser emprat. Si us plau utilitzeu-ne un altre." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "L'usuari i/o la contrasenya que heu especificat no són correctes." #: account/adapter.py:98 msgid "Please select only one." msgstr "Si us plau, seleccioneu-ne només un." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "El nou valor ha de ser diferent de l'actual." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Tingueu paciència, esteu enviant massa sol·licituds." #: account/adapter.py:826 msgid "Use your password" msgstr "Utilitzeu la vostra paraula de pas" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Utilitzar una aplicació d’autenticació o un codi" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Utilitzar una clau de seguretat" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "S'ha marcat {email} com a verificat." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "No s'ha pogut marcar {email} com a verificat." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Marqueu l’adreça de correu seleccionada com a verificada" #: account/apps.py:11 msgid "Accounts" msgstr "Comptes" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "Correu electrònic" #: account/fields.py:19 msgid "Email address" msgstr "Correu electrònic" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" "Introduïu un número de telèfon incloent el codi de país (p. ex. +1 per als " "EUA)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telèfon" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Heu d'escriure la mateixa contrasenya cada cop." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Contrasenya" #: account/forms.py:67 msgid "Remember Me" msgstr "Recordar-me" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Nom d'usuari" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Iniciar sessió" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Nom d'usuari, correu electrònic o telèfon" #: account/forms.py:117 msgid "Username or email" msgstr "Nom d'usuari o correu electrònic" #: account/forms.py:119 msgid "Username or phone" msgstr "Nom d'usuari o telèfon" #: account/forms.py:121 msgid "Email or phone" msgstr "Correu electrònic o telèfon" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Heu oblidat la contrasenya?" #: account/forms.py:287 msgid "Email (again)" msgstr "Correu electrònic (un altre cop)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Confirmació de direcció de correu electrònic" #: account/forms.py:302 msgid "Email (optional)" msgstr "Correu electrònic (opcional)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Nom d'usuari (opcional)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Heu d'escriure el mateix correu electrònic cada cop." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Contrasenya (de nou)" #: account/forms.py:591 msgid "Current Password" msgstr "Contrasenya actual" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Nova contrasenya" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Nova contrasenya (de nou)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Codi" #: account/models.py:23 msgid "user" msgstr "usuari" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "correu electrònic" #: account/models.py:31 msgid "verified" msgstr "verificat" #: account/models.py:32 msgid "primary" msgstr "principal" #: account/models.py:38 msgid "email addresses" msgstr "correus electrònics" #: account/models.py:142 msgid "created" msgstr "creat" #: account/models.py:143 msgid "sent" msgstr "enviat" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "clau" #: account/models.py:149 msgid "email confirmation" msgstr "confirmació de correu electrònic" #: account/models.py:150 msgid "email confirmations" msgstr "confirmacions de correu electrònic" #: headless/apps.py:7 msgid "Headless" msgstr "Sense capçalera" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Veure el vostre identificador d'usuari" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Veure la vostra adreça de correu electrònic" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Veure la vostra informació bàsica de perfil" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Atorgar permisos" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Els comodins no estan permesos tret que 'Permetre comodins a les URI' " "estigui activat." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "L'URI '{}' conté més d'un comodí (*). Només es permet un comodí per URI." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Els comodins només estan permesos a la part del nom de host de l'URI." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Codi d'autorització" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Codi de dispositiu" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Credencials del client" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Token de refresc" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Confidencial" #: idp/oidc/models.py:44 msgid "Public" msgstr "Públic" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Els àmbits que el client pot sol·licitar. Proporcioneu un valor per línia, " "p. ex.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "En cas que el client no especifiqui cap àmbit, s'utilitzen aquests àmbits " "per defecte. Proporcioneu un valor per línia, p. ex.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Una llista de tipus de concessió permesos. Proporcioneu un valor per línia, " "p. ex.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Una llista d'orígens permesos per a sol·licituds cross-origin, un per línia." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Permetre comodins (*) a les URI de redirecció i orígens CORS. Quan està " "activat, les URI poden contenir un sol asterisc per coincidir amb subdominis." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Una llista de tipus de resposta permesos. Proporcioneu un valor per línia, " "p. ex.: code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "client" #: idp/oidc/models.py:116 msgid "clients" msgstr "clients" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "No podeu afegir una adreça de correu electrònic a un compte protegit per " "l'autenticació de dos factors." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "No podeu desactivar el doble factor d’autenticació." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "No podeu generar codis de recuperació sense tindre activat el doble factor " "d’autenticació." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "No podeu activar l'autenticació de dos factors fins que no hàgiu verificat " "la vostra adreça de correu electrònic." #: mfa/adapter.py:141 msgid "Master key" msgstr "Clau mestra" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Clau de recanvi" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Clau n. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Codis de recuperació" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "Autenticador TOTP" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Codi de l’autenticador" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Sense contrasenya" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Habilitar operacions sense contrasenya permet iniciar la sessió utilitzant " "aquesta clau, però imposa requisits adicionals com la biometrica o protecció " "per PIN." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Ja existeix un compte associat a aquesta adreça de correu electrònic. Si us " "plau, primer identifiqueu-vos utilitzant aquest compte, i després vinculeu " "el vostre compte %s." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Token invàlid." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "El vostre compte no té una contrasenya definida." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "El vostre compte no té un correu electrònic verificat." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "No podeu desconnectar l'últim dels vostres comptes de tercers." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "El compte de xarxa social ja està connectada a un compte diferent." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Comptes de xarxes socials" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "proveïdor" #: socialaccount/models.py:53 msgid "provider ID" msgstr "ID de proveïdor" #: socialaccount/models.py:57 msgid "name" msgstr "nom" #: socialaccount/models.py:59 msgid "client id" msgstr "identificador client" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "Identificador de App o clau de consumidor" #: socialaccount/models.py:64 msgid "secret key" msgstr "clau secreta" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "" "frase secrete de API, frase secreta client o frase secreta de consumidor" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Clau" #: socialaccount/models.py:82 msgid "social application" msgstr "aplicació de xarxa social" #: socialaccount/models.py:83 msgid "social applications" msgstr "aplicacions de xarxes socials" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "darrer inici de sessió" #: socialaccount/models.py:121 msgid "date joined" msgstr "data d'incorporació" #: socialaccount/models.py:122 msgid "extra data" msgstr "dades extra" #: socialaccount/models.py:126 msgid "social account" msgstr "compte de xarxa social" #: socialaccount/models.py:127 msgid "social accounts" msgstr "comptes de xarxes socials" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) o token d'accés (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "frase secreta de token" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) o token de refrescament (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "expira el" #: socialaccount/models.py:175 msgid "social application token" msgstr "token d'aplicació de xarxa social" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "tokens d'aplicació de xarxa social" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Dades de perfil invàlides" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Iniciar sessió" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Cancel·lar" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Resposta invàlida a l'hora d'obtenir token des de “%s”. La resposta fou: " "\"%s\"." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Resposta invàlida a l'hora d'obtenir token d'accés de \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "No hi ha token de sol·licitud guardat per \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "No hi ha token d'accés guardat per \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Sense accés recursos privats de \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Resposta invàlida a l'hora d'obtenir token de sol·licitud de \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Compte inactiu" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Aquest compte està inactiu." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Hem enviat un codi a %(recipient)s. El codi caduca en breu, així que si us " "plau introduïu-lo aviat." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Confirmar" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Sol·licitar un nou codi" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Confirmeu l’accés" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "" "Sisplau, torneu-vos a autenticar per tal de mantenir el vostre compte segur." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Opcions alternatives" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Verificació de correu electrònic" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Entreu un codi d’autenticació de correu" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Utilitzar una adreça de correu electrònic diferent" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Iniciar sessió" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Introduïu el codi d'inici de sessió" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Restablir Contrasenya" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Introduïu el codi de restabliment de contrasenya" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Verificació del telèfon" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Introduïu el codi de verificació del telèfon" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Utilitzar un número de telèfon diferent" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Adreces de correu electrònic" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "" "Les següents adreces de correu electrònic estan associades al vostre compte:" #: templates/account/email.html:25 msgid "Verified" msgstr "Verificat" #: templates/account/email.html:29 msgid "Unverified" msgstr "Sense verificar" #: templates/account/email.html:34 msgid "Primary" msgstr "Principal" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Definir com a principal" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Reenviar Verificació" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Eliminar" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Afegir adreça de correu electrònic" #: templates/account/email.html:70 msgid "Add Email" msgstr "Afegir correu electrònic" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "" "Esteu segurs de voler eliminar l'adreça de correu electrònic seleccionada?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Heu rebut aquest correu electrònic perquè vostè o algú altre s'ha intentat " "registrar\n" "per obtenir un compte amb aquest correu:\n" "\n" "%(email)s\n" "\n" "No obstant això, ja existeix un compte amb aquesta adreça electrònica. En " "cas que\n" "ho hàgiu oblidat, utilitzeu el servei de recuperació de contrasenya oblidada " "per\n" "recuperar el vostre compte:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "El compte ja existeix" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Hola des de %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Gràcies per utilitzar %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Heu rebut aquest correu perquè el següent canvi ha estat fet al vostre " "compte:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Si no reconeixeu aquest canvi, premeu precaucions de manera immediata. El " "canvi ha estat originat des de:\n" "\n" "- Adreça IP: %(ip)s\n" "- Navegador: %(user_agent)s\n" "- Data: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "" "El vostre correu ha canviat. Correu anterior: %(from_email)s nou correu: " "%(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Correu electròni canviat" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "El vostre correu ha estat confirmat." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Confirmació de correu electrònic" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Heu rebut aquest missatge perquè l'usuari %(user_display)s ha proporcionat " "la vostra adreça per registrar un compte a %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "El seu codi de verificació per correu electrònic es troba a continuació. Si " "us plau, introdueixi'l a la finestra oberta del navegador." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Per confirmar que això és correcte, aneu a %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Si us plau, confirmeu la vostra adreça de correu electrònic" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "El correu %(deleted_email)s s'ha eliminat del vostre compte." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Correu esborrat" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "El seu codi d'inici de sessió es troba a continuació. Si us plau, " "introdueixi'l a la finestra oberta del navegador." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Aquest correu es pot ignorar de manera segura si vostè no ha iniciat aquesta " "acció." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Codi d'inici de sessió" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "La vostra contrasenya ha canviat." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "La vostra contrasenya ha canviat" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "El vostre codi de restabliment de contrasenya es mostra a continuació. Si us " "plau, introduïu-lo a la finestra oberta del navegador." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Codi de restabliment de contrasenya" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Heu rebut aquest correu electrònic perquè vosaltres o una altra persona heu " "sol·licitat una contrasenya per al vostre compte d'usuari.\n" "Es pot ignorar de forma segura si no es va sol·licitar el restabliment de " "contrasenya. Seguiu el següent enllaç per restablir la vostra contrasenya." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "En cas d'haver-lo oblidat, el vostre nom d'usuari és %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Correu electrònic per restablir contrasenya" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "S’ha restablert la vostra contrasenya." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "La vostra contrasenya ha canviat." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Contrasenya establerta" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Està rebent aquest correu perquè vostè, o algú altre, ha intentat accedir a " "un compte amb el correu %(email)s. No obstant això, no tenim constància de " "cap compte associat a aquest correu electrònic a la nostra base de dades." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Si era vostè, pot registrar-se per un compte amb l'enllaç a continuació." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Compte desconegut" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Adreces de correu electrònic" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Correu actual" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Canviant a" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "" "La vostra adreça de correu electrònic encara està pendent de ser verificada." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Cancel·lar el canvi" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Canviar a" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Canviar correu electrònic" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Confirmar adreça de correu electrònic" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Si us plau confirmeu que %(email)s és una " "adreça de correu electrònic de l'usuari %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "No es pot confirmar %(email)s perquè ja està confirmat en un compte diferent." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Aquest enllaç de verificació de correu electrònic ha expirat o és invàlid. " "Si us plau, sol·liciteu una nova verificació per " "correu electrònic.." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Si encara no heu creat un compte, llavors si us plau %(link)sregistreu-" "vos%(end_link)s primer." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Inicia sessió amb un clau d'accés" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Envieu-me un codi d'inici de sessió" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Tancar sessió" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Esteu segurs de voler tancar sessió?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "No podeu eliminar el vostre correu electrònic principal (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Correu electrònic de confirmació enviat a %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Heu confirmat %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Eliminat correu electrònic %(email)s." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Heu iniciat sessió exitosament com a %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Heu tancat sessió." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "S'ha enviat un codi d'inici de sessió a %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Contrasenya canviada amb èxit." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Contrasenya establerta amb èxit." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "S'ha enviat un codi de verificació a %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Heu verificat el número de telèfon %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Adreça de correu electrònic principal establerta." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Canviar Contrasenya" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Heu oblidat la vostra contrasenya?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Heu oblidat la vostra contrasenya? Introduïu el vostre correu electrònic i " "us enviarem un correu que us permetrà restablir-la." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Restablir la meva contrasenya" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Si us plau contacteu-nis si teniu algun problema per restablir la vostra " "contrasenya." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Us hem enviat un correu electrònic. Si us plau contacteu-nos si no el rebeu " "en uns minuts." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Hi ha un problema amb el token" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "L'enllaç per restablir la contrasenya és invàlid, probablement porquè ja ha " "estat utilitzat. Si us plau soliciteu restablir la contrasenya novament." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "La vostra contrasenya ha canviat." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Establir contrasenya" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Canviar telèfon" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Telèfon actual" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "El vostre número de telèfon encara està pendent de verificació." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Canviar telèfon" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Introduïu la vostra paraula de pas:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Rebreu un codi especial per iniciar sessió sense contrasenya." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Sol·licitar codi" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Opcions alternatives d'inici de sessió" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Registrar-se" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Registrar-se" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Ja teniu un compte? Si us plau %(link)sinicieu sessió%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Registra't amb un clau d'accés" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Registrar clau d'accés" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Opcions alternatives" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Registre tancat" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Ho sentim, en aquest moment el registre está tancat." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Nota" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "ja heu iniciat sessió com a %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Advertència:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Actualment no teniu cap adreça de correu electrònic definida. Hauríeu " "d'afegir una adreça de correu electrònic per poder rebre notificacions, " "restablir la contrasenya, etc." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Verifiqueu la vostra direcció de correu electrònic" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Us hem enviat un correu electrònic per la seva verificació. Seguiu l'enllaç " "per completar el procés de registre. Si us plau contacteu-nos si no el rebeu " "en uns minuts." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Aquesta part del lloc web requereix que verifiquem que\n" "sou qui dieu ser. Per això us requerim que verifiqueu la\n" "propietat del vostre correu electrònic. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Us hem enviat un correu electrònic per la vostra\n" "verificació. Si us plau accediu al link dins el correu electrònic. Si no " "veieu el correu de verificació a la vostra bústia principal, comproveu la " "carpeta d'spam. D'altra banda\n" "contacteu-nos si no el rebeu en uns minuts." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Nota: encara podeu canviar la " "vostra adreça de correu electrònic." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Missatges:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menú:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Connexions de Compte" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Autenticació de doble factor (TFA)" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sessions" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Autoritzar" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s vol accedir al vostre compte de %(site_name)s." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Introduïu el codi de dispositiu" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Introduïu el codi que es mostra al vostre dispositiu." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Continuar" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Confirmeu el dispositiu" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Si us plau, confirmeu el codi que es mostra al vostre %(client_name)s per " "autoritzar aquest dispositiu." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Denegar" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Dispositiu autoritzat" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Heu autoritzat correctament el vostre dispositiu %(client_name)s." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Dispositiu denegat" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "" "L'autorització del vostre dispositiu %(client_name)s ha estat denegada." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Error" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Mantenir la sessió iniciada" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "El teu compte està protegit per doble factor d’autenticació. Sisplau, entreu " "el codi de l’autenticador:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "S’ha generat un nou conjunt de codis de recuperació de doble factor " "d’autenticació." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "S’ha generat un nou codi de recuperació" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "App d’autenticació activada." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "App d’autenticació activada" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "App d’autenticació desactivada." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "App d’autenticació desactivada" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Una nova clau de seguretat ha estat afegida." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Clau de Seguretat Afegida" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "S'ha eliminat una clau de seguretat." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Clau de seguretat eliminada" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "App d’autenticació" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "L’autenticació està fent servir una app d’autenticació activa." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "L’app d’autenticació no està activa." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Desactivat" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Activat" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Claus de seguretat" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Has afegit %(count)s clau de seguretat." msgstr[1] "Has afegit %(count)s claus de seguretat." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "No s'ha afegit cap clau de seguretat." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Gestionar" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Afegir" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Codis de recuperació" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Hi ha %(unused_count)s de %(total_count)s codis de recuperació disponible." msgstr[1] "" "Hi ha %(unused_count)s de %(total_count)s codis de recuperació disponibles." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "No heu definit codis de recuperació." #: templates/mfa/index.html:96 msgid "View" msgstr "Vista" #: templates/mfa/index.html:102 msgid "Download" msgstr "Descarrega" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Generar" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Un nou conjunt de codis de recuperació ha estat generat." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "S'ha afegit la clau de seguretat." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "S'ha eliminat la clau de seguretat." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Entreu un codi d’autenticació:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "" "Esteu a punt de generar un nou conjunt de codis de recuperació pel vostre " "compte." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Aquesta acció invalidarà els vostres codis existents." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Esteu segur?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Codis no utilitzats" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Descarrega els codis" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Genera nous codis" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Activa la App d’autenticació" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Per protegir el vostre compte amb doble factor d’autenticació, escanejeu el " "codi QR de sota amb la vostra App d’autenticació. Llavors, introduïu el codi " "de verificació generat per l’App a sota." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Secret d’autenticador" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Podeu emmagatzemar aquest secret i usar-lo per reinstal·lar la vostra app " "d’autenticació més endavant." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Desactiva l’App d’autenticació" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Esteu a punt de desactivar la seguretat basada en app d’autenticació. " "N’esteu segurs?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Voleu confiar en aquest navegador?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Si decidiu confiar en aquest navegador, no se us demanarà un codi de " "verificació la propera vegada que inicieu sessió." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Confiar durant %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "No confiar" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Afegir clau de seguretat" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Eliminar clau de seguretat" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Esteu segurs de voler eliminar aquesta clau de seguretat?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Ús" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Clau d'accés" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Clau de seguretat" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Aquesta clau no indica si és una clau d'accés." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Sense especificar" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Afegida el %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Últim ús %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Editar" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Editar clau de seguretat" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Desar" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Crear clau d'accés" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Esteu a punt de crear una clau d'accés per al vostre compte. Com que podeu " "afegir claus addicionals més endavant, podeu utilitzar un nom descriptiu per " "distingir-les." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Crear" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Aquesta funcionalitat requereix JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Ha fallat l’autenticació de tercers" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "S'ha produït un error intentant iniciar sessió a través del vostre compte de " "xarxa social." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "Podeu iniciar sessió amb algun dels següents comptes de tercers:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Actualment no tens cap compte de tercers associat a aquest compte." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Afegir un compte de tercers" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "" "El compte de tercers des de %(provider)s ha estat vinculat al teu compte." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Compte de tercers vinculat" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" "El compte de tercers des de %(provider)s ha estat desvinculat al teu compte." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Compte de tercers desvinculat" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Connectar %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Esteu a punt de connectar un nou compte extern des de %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Iniciar sessió via %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" "Esteu a punt d'iniciar sessió utilitzant un compte extern des de " "%(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Inici de sessió cancel·lat" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Heu decidit cancel·lar l'inici de sessió al vostre lloc web utilitzant un " "dels vostres comptes existents. Si ha estat un error, si us plau inicieu sessió." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "El compte de xarxa social ha estat connectat." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "El compte de xarxa social s'ha desconnectat." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Esteu a punt d'utilitzar el vostre compte de %(provider_name)s per iniciar " "sessió a\n" "%(site_name)s. Com a pas final, si us plau completeu el següent formulari:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "O utilitzeu un compte de tercers" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Sortiu de totes les altres sessions." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Ha començat a les" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "Adreça IP" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Navegador" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Vist darreranent" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Actual" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Tanca les altres sessions" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Sessions de l’usuari" #: usersessions/models.py:94 msgid "session key" msgstr "clau de sessió" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Connexions de Compte" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "La contrasenya ha de contenir al menys {0} caràcters." #, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Has rebut aquest correu electrònic perquè vosaltres o algú altre heu " #~ "sol·licitat una\n" #~ "contrasenya per al vostre compte d'usuari. Tot i això, no tenim cap " #~ "registre d'un usuari\n" #~ "amb correu electrònic %(email)s a la nostra base de dades.\n" #~ "\n" #~ "Aquest correu es pot ignorar de forma segura si no heu sol·licitat un " #~ "canvi de contrasenya.\n" #~ "\n" #~ "Si heu estat vosaltres, podeu registrar un compte d'usuari utilitzant el " #~ "link de sota." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "" #~ "Les següents adreces de correu electrònic estan associades al vostre " #~ "compte:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Confirmar adreça de correu electrònic" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Si us plau, inicieu sessió amb un\n" #~ "compte d'una altra xarxa social. O %(link)sregistreu-vos \n" #~ "com a usuari de %(site_name)s i inicieu sessió a continuació:" #~ msgid "or" #~ msgstr "o" #~ msgid "change password" #~ msgstr "canviar la contrasenya" #~ msgid "OpenID Sign In" #~ msgstr "Iniciar sessió amb OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "Aquest correu electrònic ja està associat amb un altre compte." ================================================ FILE: allauth/locale/cs/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Tomas Marcik , 2013. # Beda Kosata , 2018. # Filip Dobrovolny , 2023. # msgid "" msgstr "" "Project-Id-Version: 0.55\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-07-30 18:06+0000\n" "Last-Translator: Jakub Boukal \n" "Language-Team: Czech \n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n " "<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" "X-Generator: Weblate 5.13-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Účet je v tuto chvíli neaktivní." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Nemůžete odstranit primární e-mailovou adresu." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Tento e-mail je již k tomuto účtu přiřazen." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Zadaný e-mail nebo heslo není správné." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Zadané telefonní číslo nebo heslo není správné." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Uživatel s tímto e-mailem je již registrován." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Prosím, zadejte svoje současné heslo." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Nesprávný kód." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Nesprávné heslo." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Neplatný klíč." #: account/adapter.py:79 msgid "Invalid login." msgstr "Neplatné přihlášení." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Token pro reset hesla není platný." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Nelze přidat více než %d e-mailových adres." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Uživatel s tímto telefonním číslem je již registrován." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Příliš mnoho pokusů o přihlášení. Zkuste to prosím později." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "E-mailová adresa není přiřazena k žádnému uživatelskému účtu." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Telefonní číslo není přiřazeno k žádnému uživatelskému účtu." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Vaše primární e-mailová adresa musí být ověřena." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Toto uživatelské jméno nemůže být zvoleno. Prosím, zvolte si jiné." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Zadané uživatelské jméno nebo heslo není správné." #: account/adapter.py:98 msgid "Please select only one." msgstr "Vyberte prosím pouze jednu možnost." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Nová hodnota se musí lišit od aktuální." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Buďte trpěliví, odesíláte příliš mnoho požadavků." #: account/adapter.py:826 msgid "Use your password" msgstr "Zadejte své heslo" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Použijte ověřovací aplikaci nebo kód" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Zadejte tajný klíč" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "E-mailová adresa {email} byla označena jako ověřená." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Nepodařilo se označit {email} jako ověřenou." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Označ vybrané e-mailové adresy jako ověřené" #: account/apps.py:11 msgid "Accounts" msgstr "Účty" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-mail" #: account/fields.py:19 msgid "Email address" msgstr "E-mailová adresa" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Zadejte telefonní číslo včetně předvolby země (např. +1 pro USA)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefon" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Hesla se musí shodovat." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Heslo" #: account/forms.py:67 msgid "Remember Me" msgstr "Zapamatovat" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Uživatelské jméno" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Přihlášení" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Uživatelské jméno, e-mail nebo telefon" #: account/forms.py:117 msgid "Username or email" msgstr "Uživatelské jméno nebo e-mail" #: account/forms.py:119 msgid "Username or phone" msgstr "Uživatelské jméno nebo telefon" #: account/forms.py:121 msgid "Email or phone" msgstr "E-mail nebo telefon" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Zapomněli jste heslo?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-mail (znovu)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Potrvzení e-mailové adresy" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-mail (nepovinné)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Uživatelské jméno (nepovinné)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Vložené e-maily se musí shodovat." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Heslo (znovu)" #: account/forms.py:591 msgid "Current Password" msgstr "Současné heslo" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Nové heslo" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Nové heslo (znovu)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Kód" #: account/models.py:23 msgid "user" msgstr "uživatel" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "e-mailová adresa" #: account/models.py:31 msgid "verified" msgstr "ověřeno" #: account/models.py:32 msgid "primary" msgstr "primární" #: account/models.py:38 msgid "email addresses" msgstr "e-mailové adresy" #: account/models.py:142 msgid "created" msgstr "vytvořeno" #: account/models.py:143 msgid "sent" msgstr "odeslaný" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "klíč" #: account/models.py:149 msgid "email confirmation" msgstr "Potvrzovací e-mail" #: account/models.py:150 msgid "email confirmations" msgstr "Ověřovací e-maily" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Zobrazit vaše ID uživatele" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Zobrazit vaši e-mailovou adresu" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Zobrazit vaše základní profilové informace" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Udělit oprávnění" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Zástupné znaky nejsou povoleny, pokud není povolena možnost 'Povolit " "zástupné znaky v URI'." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' obsahuje více než jeden zástupný znak (*). Na jedno URI je povolen " "pouze jeden zástupný znak." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Zástupné znaky jsou povoleny pouze v části URI s názvem hostitele." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Autorizační kód" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Kód zařízení" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Přihlašovací údaje klienta" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Obnovovací token" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Důvěrný" #: idp/oidc/models.py:44 msgid "Public" msgstr "Veřejný" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Rozsahy, které může klient požadovat. Uveďte jednu hodnotu na řádek, např.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Pokud klient neuvede žádný rozsah, použijí se tyto výchozí rozsahy. Uveďte " "jednu hodnotu na řádek, např.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Seznam povolených typů udělení. Uveďte jednu hodnotu na řádek, např.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "Seznam povolených zdrojů pro cross-origin požadavky, jeden na řádek." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Povolit zástupné znaky (*) v přesměrovacích URI a zdrojích CORS. Pokud je " "povoleno, mohou URI obsahovat jednu hvězdičku pro shodu subdomén." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Seznam povolených typů odpovědí. Uveďte jednu hodnotu na řádek, např.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "klient" #: idp/oidc/models.py:116 msgid "clients" msgstr "klienti" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Nelze přidat e-mailovou adresu k účtu chráněnému dvoufaktorovouautentizací." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Dvoufaktorové ověřování nelze deaktivovat." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Nelze generovat kódy pro obnovení bez aktivovaného dvoufaktorového ověřování." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Nemůžete aktivovat dvoufaktorovou autentizaci, dokud nepotvrdíte svoue-" "mailovou adresu." #: mfa/adapter.py:141 msgid "Master key" msgstr "Hlavní klíč" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Záložní klíč" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Klíč #{number}" #: mfa/apps.py:9 msgid "MFA" msgstr "2FA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Záchranné kódy" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP Autentifikátor" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Kód autentifikátoru" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Bez hesla" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Povolení možnosti bez hesla vám umožní přihlásit se pouze pomocí tohoto " "klíče, ale klade další požadavky, jako je biometrie nebo ochrana PIN." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Účet s touto e-mailovou adresou již existuje. Prosím přihlaste se nejdříve " "pod tímto účtem a potom připojte svůj %s účet." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Neplatný token." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Váš účet nemá nastavené heslo." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Váš účet nemá žádný ověřený e-mail." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Nemůžete odpojit svůj poslední externí účet." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Externí účet je již spojen s jiným účtem." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Účty sociálních sítí" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "poskytovatel" #: socialaccount/models.py:53 msgid "provider ID" msgstr "ID poskytovatele" #: socialaccount/models.py:57 msgid "name" msgstr "jméno" #: socialaccount/models.py:59 msgid "client id" msgstr "id klienta" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "App ID nebo uživatelský klíč" #: socialaccount/models.py:64 msgid "secret key" msgstr "tajný klíč" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "tajný API klíč, tajný klientský klíč nebo uživatelský tajný klíč" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Klíč" #: socialaccount/models.py:82 msgid "social application" msgstr "sociální aplikace" #: socialaccount/models.py:83 msgid "social applications" msgstr "sociální aplikace" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "poslední přihlášení" #: socialaccount/models.py:121 msgid "date joined" msgstr "datum registrace" #: socialaccount/models.py:122 msgid "extra data" msgstr "extra data" #: socialaccount/models.py:126 msgid "social account" msgstr "účet sociální sítě" #: socialaccount/models.py:127 msgid "social accounts" msgstr "účty sociálních sítí" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) nebo přístupový token (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "tajný token" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) nebo token pro obnovu (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "vyprší" #: socialaccount/models.py:175 msgid "social application token" msgstr "token sociální aplikace" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "tokeny sociálních aplikací" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Neplatná data profilu" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Přihlášení" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Zrušit" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "Chyba při odesílání požadavku: \"%s\". Odpoveď byla: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Chyba při získávání přístupového klíče od \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Není uložen žádný požadavkový klíč pro: \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Není uložen žádný přístupový klíč pro: \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Není přístup k privátním zdrojům: \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Chyba při získávání požadavkového klíče \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Neaktivní účet" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Tento účet není aktivní." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Na adresu %(recipient)s jsme poslali kód. Jeho platnost brzy vyprší, zadejte " "ho prosím včas." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Potvrdit" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Požádat o nový kód" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Potvrdit přístup" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Pro ochranu účtu se prosím znovu ověřte." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Alternativní možnosti" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Ověření e-mailu" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Zadejte ověřovací kód e-mailu" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Použít jinou e-mailovou adresu" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Přihlásit se" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Zadejte Ověřovací Kód" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Reset hesla" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Zadejte kód pro obnovení hesla" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Ověření telefonního čísla" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Zadejte ověřovací kód telefonu" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Použít jiné telefonní číslo" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "E-mailové adresy" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "K vašemu účtu jsou přiřazeny tyto e-mailové adresy:" #: templates/account/email.html:25 msgid "Verified" msgstr "Ověřeno" #: templates/account/email.html:29 msgid "Unverified" msgstr "Neověřeno" #: templates/account/email.html:34 msgid "Primary" msgstr "Primární" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Zvolit jako primární" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Znovu zaslat oveřovací e-mail" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Odstranit" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Přidat e-mailovou adresu" #: templates/account/email.html:70 msgid "Add Email" msgstr "Přidat e-mail" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Opravdu chcete odstranit zvolené e-mailové adresy?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Tento e-mail jste obdrželi, protože vy nebo někdo jiný se pokusil " "registrovat účet s \n" "použitím e-mailové adresy:\n" "\n" "%(email)s\n" "\n" "Ale účet s touto e-mailovou adresou již existuje. Pokud jste na to " "zapomněli, \n" "použijte prosím postup obnovení hesla k obnovení vašeho účtu:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Účet již existuje" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Pozdrav z %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Děkujeme, že používáte %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Tento e-mail jste obdrželi, protože ve vašem účtu byla provedena následující " "změna:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Pokud tuto změnu nepoznáváte, prosím okamžitě proveďte náležitá bezpečnostní " "opatření. Změna vašeho účtu pochází z:\n" "\n" "- IP adresa: %(ip)s\n" "- Prohlížeč: %(user_agent)s\n" "- Datum %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Váš e-mail byl změněn z %(from_email)s na %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Email změněn" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Váš email byl potvrzen." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Potvrzení E-mailu" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Tento e-mail jste obdrželi protože uživatel %(user_display)s zadal vaši e-" "mailovou adresu k registraci účtu na stránkách %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Níže najdete váš ověřovací kód. Zadejte jej do otevřeného okna prohlížeče." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Pro potvrzení, že je to v pořádku, pokračujte na %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Potvrďte prosím svou e-mailovou adresu" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "E-mailová adresa %(deleted_email)s byla odstraněna z vašeho účtu." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Email odstraněn" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Níže najdete váš přihlašovací kód. Zadejte jej do otevřeného okna prohlížeče." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Tento e-mail může být bezpečně ignorován, pokud jste tuto akci nezahájili vy." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Přihlašovací kód" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Vaše heslo bylo změněno." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Heslo změněno" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Níže najdete váš kód pro obnovení hesla. Zadejte jej do otevřeného okna " "prohlížeče." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Kód pro obnovení hesla" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Tento e-mail jste obdrželi protože jste vy nebo někdo jiný zažádal o změnu " "hesla uživatelského účtu.\n" "Pokud jste to nebyli vy, můžete tento e-mail ignorovat. Pokud ano, klikněte " "na odkaz níže pro změnu vašeho hesla." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "" "Pro případ, že byste zapomněli, vaše uživatelské jméno je %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "E-mail pro reset hesla" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Vaše heslo bylo obnoveno." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Vaše heslo bylo nastaveno." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Nastavení hesla" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Tento e-mail jste obdrželi, protože jste se vy nebo někdo jiný pokusili o " "přístup k účtu s e-mailem %(email)s. V naší databázi však žádný záznam o " "takovém účtu nemáme." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Pokud jste to byli vy, můžete si účet zaregistrovat pomocí odkazu níže." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Neznámý účet" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "E-mailové adresa" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Aktuální email" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Změněn na" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Vaše primární e-mailová adresa stále čeká na ověření." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Zrušit změnu" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Změnit na" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Změnit E-mail" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Potvrzení e-mailové adresy" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Prosím, potvrďte, že %(email)s je e-mailová " "adresa pro uživatele %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "Nelze potvrdit %(email)s, protože již byl spojen s jiným účtem." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Tento ověřovací odkaz již vypršel nebo není správný. Prosím, zažádejte si o nový." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Pokud jste si ještě nevytvořili účet, nejprve se " "%(link)szaregistrujte%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Přihlaste se pomocí přístupového klíče" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Pošlete mi přihlašovací kód" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Odhlásit se" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Jste si jisti, že se chcete odhlásit?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Nemůžete odstranit primární e-mailovou adresu (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Ověření e-mailu posláno na %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Ověřili jste %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "E-mailová adresa %(email)s byla odebrána." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Úspěšně přihlášen jako %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Právě jste byl odhlášen." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Přihlašovací kód byl zaslán na adresu %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Heslo bylo úspěšně změněno." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Heslo bylo úspěšně nastaveno." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Ověřovací kód byl zaslán na číslo %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Ověřili jste telefonní číslo %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Primární e-mail byla nastavena." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Změnit heslo" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Zapomenuté heslo?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Zapomněli jste heslo? Zadejte prosím svoji e-mailovou adresu a do e-mailové " "schránky Vám přijde návod na jeho obnovu." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Resetovat moje heslo" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Prosím, kontaktujte nás, pokud máte jakékoliv potíže s resetováním hesla." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Poslali jsme vám e-mail. Pokud jste ho neobdrželi, zkontrolujte prosím " "složku s nevyžádanou poštou (spam). V opačném případě nás kontaktujte, pokud " "ho neobdržíte do několika minut." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Chybný klíč" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Odkaz na resetování hesla byl neplatný, možná proto, že již byl použit. " "Požádejte prosím o nový reset hesla." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Vaše heslo je nyní změněno." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Nastavit heslo" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Změnit telefon" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Aktuální telefon" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Vaše telefonní číslo stále čeká na ověření." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Změnit telefon" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Zadejte své heslo:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Obdržíte speciální kód pro přihlášení bez hesla." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Požádat kód" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Další možnosti přihlášení" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Zaregistrovat se" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Zaregistrovat se" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Máte již účet? %(link)sPřihlašte se%(end_link)s, prosím." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Zaregistrujte se pomocí přístupového klíče" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Registrace přístupového klíče" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Další možnosti" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Registrace je uzavřena" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Omlouváme se, ale registrace je momentálně uzavřena." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Poznámka" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Již jste přihlášen jako %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Varování:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "V současné chvíli nemáte nastaveny žádné e-mailové adresy. Prosím, uložte si " "k účtu alespoň jeden e-mail, abyste moli dostávat upozornění nebo mohli " "použít funkci zapomenutého hesla apod." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Ověřte svoji e-mailovou adresu" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Byl vám zaslán ověřovací e-mail. Následujte odkaz v e-mailu pro dokončení " "registračního procesu. Pokud jste ho neobdrželi, zkontrolujte prosím složku " "s nevyžádanou poštou (spam). Neváhejte nás kontaktovat v případě, pokud e-" "mail do několika minut neobdržíte." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Tato část stránek vyžaduje ověření,\n" "že jste ten, kdo tvrdíte. K těmto účelům požadujeme\n" "aby jste ověřil vlastnictví své e-mailové adresy. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Zaslali jsme vám email pro oveření.\n" "Prosím, klikněte na odkaz uvnitř e-mailu. Pokud jste ho neobdrželi, " "zkontrolujte prosím složku s nevyžádanou poštou (spam).\n" "Neváhejte nás kontaktovat v případě, pokud e-mail nedostanete do několika " "minut." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Poznámka: stále můžete změnit " "vaši e-mailovou adresu.." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Zprávy:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menu:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Propojení účtu" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Dvoufaktorová autentizace" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Relace" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Autorizovat" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s chce získat přístup k vašemu účtu %(site_name)s." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Zadejte kód zařízení" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Zadejte kód zobrazený na vašem zařízení." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Pokračovat" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Potvrdit zařízení" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Potvrďte prosím kód zobrazený na vašem %(client_name)s pro autorizaci tohoto " "zařízení." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Zamítnout" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Zařízení autorizováno" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Úspěšně jste autorizovali vaše zařízení %(client_name)s." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Zařízení zamítnuto" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Autorizace vašeho zařízení %(client_name)s byla zamítnuta." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Chyba" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Zůstat přihlášen" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Váš účet je chráněn dvoufaktorovou autentizací. Prosím, zadejte autentizační " "kód:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Byla vygenerována nová sada obnovovacích kódů dvoufaktorové autentizace." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Nové obnovovací kódy vygenerovány" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Autentifikátor byl aktivován." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Autentifikátor byl aktivován" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Autentifikátor byl deaktivován." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Autentifikátor byl deaktivován" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Byl přidán nový bezpečnostní klíč." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Bezpečnostní klíč přidán" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Bezpečnostní klíč byl odstraněn." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Bezpečnostní klíč odstraněn" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Autentifikátor" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Autentizace pomocí autentifikátoru je aktivní." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Autentifikátor není aktivní." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Deaktivovat" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Aktivovat" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Bezpečnostní klíče" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Přidali jste %(count)s bezpečnostní klíč." msgstr[1] "Přidali jste %(count)s bezpečnostní klíče." msgstr[2] "Přidali jste %(count)s bezpečnostního klíče." msgstr[3] "Přidali jste %(count)s bezpečnostních klíčů." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Nebyly přidány žádné bezpečnostní klíče." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Spravovat" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Přidat" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Záchranné kódy" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Z dostupných záchranných kódů je použit %(unused_count)s z celkového počtu " "%(total_count)s kódů." msgstr[1] "" "Z dostupných záchranných kódů jsou použity %(unused_count)s z celkového " "počtu %(total_count)s kódů." msgstr[2] "" "Z dostupných záchranných kódů je použito %(unused_count)s z celkového počtu " "%(total_count)s kódů." msgstr[3] "" "Z dostupných záchranných kódů je použitých %(unused_count)s z celkového " "počtu %(total_count)s kódů." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Nejsou nastaveny žádné záchranné kódy." #: templates/mfa/index.html:96 msgid "View" msgstr "Zobrazit" #: templates/mfa/index.html:102 msgid "Download" msgstr "Stáhnout" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Generovat" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Byla vygenerována nová sada záchranných kódů." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Bezpečnostní klíč přidán." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Bezpečnostní klíč odstraněn." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Zadejte ověřovací kód:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Chystáte se vygenerovat novou sadu obnovovacích kódů pro váš účet." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Tato akce zruší platnost vašich stávajících kódů." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Jste si jistý?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Nepoužité kódy" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Stáhnout kódy" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Generovat nové kódy" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Aktivovat Autentifikátor" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Chcete-li svůj účet chránit pomocí dvoufaktorového ověřování, naskenujte " "pomocí autentikační aplikace uvedený QR kód. Poté níže zadejte ověřovací kód " "vygenerovaný aplikací." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Klíč autentifikátoru" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Tento klíč můžete uložit a později použít k opětovné instalaci aplikace " "autentikátoru." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Deaktivovat Autentifikátor" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Chystáte se deaktivovat autentizaci pomocí autentifikátoru. Jste si jisti?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Důvěřovat tomuto prohlížeči?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Pokud se rozhodnete důvěřovat tomuto prohlížeči, při příštím přihlášení " "nebudete požádáni o ověřovací kód." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Důvěřovat po dobu %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Nedůvěřovat" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Přidat tajný klíč" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Odebrat tajný klíč" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Jste si jisti, že se chcete odebrat tajný klíč?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Použito" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Přístupový klíč" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Bezpečnostní klíč" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Tento klíč neoznačuje, zda se jedná o přístupový klíč." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Nespecifikováno" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Přidáno %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Naposledy použito %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Upravit" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Upravit bezpečnostní klíč" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Uložit" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Vytvořte přístupový klíč" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Chystáte se vytvořit přístupový klíč pro svůj účet. Protože později můžete " "přidat další klíče, můžete klíče odlišit pomocí popisného názvu." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Vytvořit" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Tato funkce vyžaduje JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Přihlášení pomocí externího účtu selhalo" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "Nastala chyba při přihlašování pomocí externího účtu." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "Můžete se přihlásit pomocí jakéhokoliv následujícího externího účtu:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "V současné chvíli nemáte připojeny žádné další účty." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Přidejte další externí účet" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "K vašemu účtu byl připojen další účet od %(provider)s." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Externí účet přidán" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "Externí účet od %(provider)s byl odpojen od vašeho účtu." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Externí účet odpojen" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Připojit %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Chystáte se připojit nový externí účet od %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Přihlásit se pomocí %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "Chystáte se přihlásit pomocí externího účtu od %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Přihlášení zrušeno" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Rozhodli jste se zrušit přihlašování jednoho z vašich účtů. Pokud je to " "omylem, následujte přihlášení." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Externí účet byl připojen." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Externí účet byl odpojen." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Chystáte se použít vášeho %(provider_name)s účtu k přihlášení na naše " "stránky \n" "%(site_name)s. Jako poslední krok, prosím, vyplňte následující formulář:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Nebo použijte externí účet" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Odhlášen ze všech ostatních relací." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Začala v" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP adresa" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Prohlížeč" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Napoposledy použita v" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Aktuální" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Odhlásit ostatní relace" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Uživatelské relace" #: usersessions/models.py:94 msgid "session key" msgstr "klíč relace" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Propojení účtu" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Heslo musí obsahovat minimálně {0} znaků." #, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Tento e-mail jste obdrželi protože jste vy nebo někdo jiný zažádal o " #~ "změnu \n" #~ "hesla uživatelského účtu. Nicméně, nemáme žádný záznam o uživateli s \n" #~ "e-mailem %(email)s v naší databázi.\n" #~ "\n" #~ "Tento e-mail můžete bezpečně ignorovat, pokud jste nezažádali o změnu " #~ "hesla.\n" #~ "\n" #~ "Jestliže jste to byli vy, můžete se zaregistrovat na stránkách pomocí " #~ "odkazu \n" #~ "níže." #~ msgid "The following email address is associated with your account:" #~ msgstr "K vašemu účtu je přiřazena tato e-mailová adresa:" #~ msgid "Change Email Address" #~ msgstr "Změna e-mailové adresy" #~ msgid "" #~ "To safeguard the security of your account, please enter your password:" #~ msgstr "Pro zabezpečení vašeho účtu, prosím, zadejte vaše heslo:" #, fuzzy #~ msgid "Regenerate" #~ msgstr "Generovat" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Přihlašte se prosím výběrem jednoho\n" #~ "z vašich účtů třetích stran. Nebo se zaregistruje na stránky %(site_name)s a " #~ "přihlašte se níže:" #~ msgid "or" #~ msgstr "nebo" #~ msgid "change password" #~ msgstr "změnit heslo" #~ msgid "OpenID Sign In" #~ msgstr "Přihlášení OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "Tento e-mail je již přiřazen k jinému účtu." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Zaslali jsme vám e-mail. Prosím, kontaktujte nás, pokud ho nedostanete do " #~ "několika minut." #~ msgid "The provided password is not valid." #~ msgstr "Použité heslo není platné." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "Zadané přihlašovací údaje nejsou správné." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "" #~ "Uživatelské jméno může obsahovat pouze písmena, číslice a znaky @/./+/-/_." #~ msgid "This username is already taken. Please choose another." #~ msgstr "Toto uživatelské jméno je již zvoleno. Prosím, vyberte si jiné." #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "Potvrdili jste e-mailovou adresu %(email)s uživateli %(user_display)s." #~ msgid "Thanks for using our site!" #~ msgstr "Děkujeme za využívání našich stránek!" #~ msgid "Confirmation email sent to %(email)s" #~ msgstr "Ověřovací e-mail byl zaslán: %(email)s" #~ msgid "Delete Password" #~ msgstr "Smazat heslo" #~ msgid "" #~ "You may delete your password since you are currently logged in using " #~ "OpenID." #~ msgstr "Můžete si smazat heslo, protože používáte jiné způsoby přihlášení." #~ msgid "delete my password" #~ msgstr "Odstanit moje heslo" #~ msgid "Password Deleted" #~ msgstr "Heslo bylo odstraněno" ================================================ FILE: allauth/locale/da/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2024-04-20 21:40+0200\n" "Last-Translator: b'Tuk Bredsdorff '\n" "Language-Team: \n" "Language: da\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 2.1.1\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Denne konto er i øjeblikket inaktiv." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Du kan ikke fjerne din primære emailadresse." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Denne emailadresse er allerede knyttet til denne konto." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Den angivne emailadresse og/eller adgangskode er ikke korrekt." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Det angivne telefonnummer og/eller adgangskode er ikke korrekt." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "En bruger er allerede registreret med denne emailadresse." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Indtast din nuværende adgangskode." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Forkert kode." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Forkert adgangskode." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Ugyldig eller udløbet nøgle." #: account/adapter.py:79 msgid "Invalid login." msgstr "Ugyldigt login." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Token for nulstilling af adgangskode var ugyldig." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Du kan ikke tilføje mere end %d emailadresser." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "En bruger er allerede registreret med dette telefonnummer." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Der er for mange mislykkede logonforsøg. Prøv igen senere." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Emailadressen er ikke tildelt til nogen brugerkonto." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Telefonnummeret er ikke tildelt til nogen brugerkonto." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Din primære emailadresse skal bekræftes." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Brugernavn kan ikke bruges. Brug venligst et andet brugernavn." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Det angivne brugernavn og/eller adgangskoden er ikke korrekt." #: account/adapter.py:98 msgid "Please select only one." msgstr "Vælg kun én." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Den nye værdi skal være anderledes end den nuværende." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Hav tålmodighed, du sender for mange anmodninger." #: account/adapter.py:826 msgid "Use your password" msgstr "Brug din adgangskode" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Brug autentificeringsapp eller kode" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Brug en sikkerhedsnøgle" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "Markerede {email} som bekræftet." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Kunne ikke markere {email} som bekræftet." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Marker valgte emailadresser som bekræftede" #: account/apps.py:11 msgid "Accounts" msgstr "Konti" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "Email" #: account/fields.py:19 msgid "Email address" msgstr "Emailadresse" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Indtast et telefonnummer inklusive landekode (f.eks. +1 for USA)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefon" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Du skal skrive den samme adgangskode hver gang." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Adgangskode" #: account/forms.py:67 msgid "Remember Me" msgstr "Husk mig" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Brugernavn" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Bruger" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Brugernavn, email eller telefon" #: account/forms.py:117 msgid "Username or email" msgstr "Brugernavn eller email" #: account/forms.py:119 msgid "Username or phone" msgstr "Brugernavn eller telefon" #: account/forms.py:121 msgid "Email or phone" msgstr "Email eller telefon" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Glemt din adgangskode?" #: account/forms.py:287 msgid "Email (again)" msgstr "Email (igen)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Bekræftelse af emailadresse" #: account/forms.py:302 msgid "Email (optional)" msgstr "Email (valgfri)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Brugernavn (valgfrit)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Du skal skrive den samme emailadresse hver gang." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Adgangskode (igen)" #: account/forms.py:591 msgid "Current Password" msgstr "Nuværende adgangskode" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Ny adgangskode" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Ny adgangskode (igen)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Kode" #: account/models.py:23 msgid "user" msgstr "bruger" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "emailadresse" #: account/models.py:31 msgid "verified" msgstr "bekræftet" #: account/models.py:32 msgid "primary" msgstr "primær" #: account/models.py:38 msgid "email addresses" msgstr "emailadresser" #: account/models.py:142 msgid "created" msgstr "oprettet" #: account/models.py:143 msgid "sent" msgstr "sendt" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "nøgle" #: account/models.py:149 msgid "email confirmation" msgstr "emailbekræfelse" #: account/models.py:150 msgid "email confirmations" msgstr "emailbekræftelse" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Se dit bruger-ID" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Se din emailadresse" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Se dine grundlæggende profiloplysninger" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Giv tilladelser" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Jokertegn er ikke tilladt, medmindre 'Tillad URI-jokertegn' er aktiveret." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI'en '{}' indeholder mere end ét jokertegn (*). Kun ét jokertegn per URI " "er tilladt." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Jokertegn er kun tilladt i værtsnavnsdelen af URI'en." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Autorisationskode" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Enhedskode" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Klientlegitimationsoplysninger" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Opdater token" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Fortrolig" #: idp/oidc/models.py:44 msgid "Public" msgstr "Offentlig" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "De scope(s), som klienten har tilladelse til at anmode om. Angiv én værdi " "pr. linje, f.eks.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Hvis klienten ikke angiver nogen scope, bruges disse standardscopes. Angiv " "én værdi pr. linje, f.eks.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "En liste over tilladte grant-typer. Angiv én værdi pr. linje, f.eks.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "En liste over tilladte oprindelser for cross-origin anmodninger, én pr. " "linje." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Tillad jokertegn (*) i omdirigerings-URI'er og CORS-oprindelser. Når " "aktiveret kan URI'er indeholde en enkelt stjerne for at matche underdomæner." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "En liste over tilladte responstyper. Angiv én værdi pr. linje, f.eks.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "klient" #: idp/oidc/models.py:116 msgid "clients" msgstr "klienter" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Du kan ikke tilføje en emailadresse til en konto beskyttet af to-faktor-" "autentificering." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Du kan ikke deaktivere to-faktor-autentificering." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Du kan ikke generere gendannelseskoder uden at have to-faktor-" "autentificering aktiveret." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Du kan ikke aktivere to-faktor-autentificering, før du har bekræftet din e-" "mailadresse." #: mfa/adapter.py:141 msgid "Master key" msgstr "Hovednøgle" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Backup-nøgle" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Nøgle nr. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Gendannelseskoder" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP-autentificering" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Autentificeringskode" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Adgangskode-fri" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Aktivering af adgangskode-fri drift giver dig mulighed for at logge ind med " "kun denne nøgle, men stiller yderligere krav såsom biometri eller PIN-" "beskyttelse." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "En konto med denne emailadresse eksisterer allerede. Log venligst ind med " "den konto først og tilknyt din %s konto derefter." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Ugyldigt token." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Der er ikke oprettet noget password til din konto." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Din konto har ikke nogen bekræftet emailadresse." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Du kan ikke afbryde din sidste resterende tredjepartskonto." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Tredjepartskontoen er allerede tilsluttet en anden konto." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Sociale konti" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "udbyder" #: socialaccount/models.py:53 msgid "provider ID" msgstr "udbyder-ID" #: socialaccount/models.py:57 msgid "name" msgstr "navn" #: socialaccount/models.py:59 msgid "client id" msgstr "klient id" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "App ID, eller konsumer nøgle" #: socialaccount/models.py:64 msgid "secret key" msgstr "hemmelig nøgle" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API hemmelighed, klient hemmelighed eller konsumet hemmelighed" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Nøgle" #: socialaccount/models.py:82 msgid "social application" msgstr "social applikation" #: socialaccount/models.py:83 msgid "social applications" msgstr "sociale applikationer" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "sidste log ind" #: socialaccount/models.py:121 msgid "date joined" msgstr "dato oprettet" #: socialaccount/models.py:122 msgid "extra data" msgstr "ekstra data" #: socialaccount/models.py:126 msgid "social account" msgstr "social konto" #: socialaccount/models.py:127 msgid "social accounts" msgstr "sociale konti" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "“oauth_token” (OAuth1) eller adgangstoken (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "token hemmelighed" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "“oauth_token_secret” (OAuth1) eller fornyelsestoken (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "udløber den" #: socialaccount/models.py:175 msgid "social application token" msgstr "socialt applikationstoken" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "sociale applikationstokener" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Ugyldig profildata" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Log ind" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Annuller" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Ugyldig respons under forsøg på at hente request token fra \"%s\". Respons " "var: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Ugyldig respons under forsøg på at hente adgangstoken fra “%s”." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Intet request token gemt for “%s”." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Intet adgangstoken gemt for “%s”." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Ingen adgang til private ressourcer på “%s”." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Ugyldig respons under forsøg på at hente request token fra “%s”." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Inaktiv konto" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Denne konto er inaktiv." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Vi har sendt en kode til %(recipient)s. Koden udløber snart, så indtast den " "venligst hurtigt." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Bekræft" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Anmod om ny kode" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Bekræft adgang" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Genautentificer venligst for at beskytte din konto." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Alternative muligheder" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Emailbekræftelse" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Indtast emailbekræftelseskode" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Brug en anden emailadresse" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Log ind" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Indtast login-kode" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Nulstil password" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Indtast adgangskode-nulstillingskode" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Telefonbekræftelse" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Indtast telefonbekræftelseskode" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Brug et andet telefonnummer" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Emailadresser" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "De følgende emailadresser er tilknyttet din konto:" #: templates/account/email.html:25 msgid "Verified" msgstr "Bekæftet" #: templates/account/email.html:29 msgid "Unverified" msgstr "Ubekræftet" #: templates/account/email.html:34 msgid "Primary" msgstr "Primær" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Gør primær" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Send bekræftelse igen" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Fjern" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Tilføj emailadresse" #: templates/account/email.html:70 msgid "Add Email" msgstr "Tilføj email" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Vil du virkelig fjerne den valgte emailadresse?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Du modtager denne email, fordi du eller en anden har forsøgt at tilmelde sig " "en\n" "konto med emailadressen:\n" "\n" "%(email)s\n" "\n" "Der findes dog allerede en konto med denne emailadresse. Hvis du har\n" "glemt dette, brug venligst proceduren for glemt adgangskode for at gendanne\n" "din konto:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Konto eksisterer allerede" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Hej fra %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Tak fordi du bruger %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Du modtager denne email, fordi følgende ændring er blevet foretaget på din " "konto:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Hvis du ikke genkender denne ændring, skal du straks tage passende " "sikkerhedsforanstaltninger. Ændringen af din konto stammer fra:\n" "\n" "- IP-adresse: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Dato: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Din email er blevet ændret fra %(from_email)s til %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Email ændret" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Din email er blevet bekræftet." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Emailbekræftelse" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Du modtager denne email, fordi brugeren %(user_display)s har angivet din " "emailadresse for at registrere en konto på %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Din emailbekræftelseskode er angivet nedenfor. Indtast den venligst i dit " "åbne browservindue." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "For at bekræfte at dette er korrekt, gå til %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Bekræft venligst din emailadresse" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "Emailadressen %(deleted_email)s er blevet fjernet fra din konto." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Email fjernet" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Din login-kode er angivet nedenfor. Indtast den venligst i dit åbne " "browservindue." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Denne email kan ignoreres, hvis du ikke selv har startet denne handling." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Login-kode" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Din adgangskode er blevet ændret." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Adgangskode ændret" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Din adgangskode-nulstillingskode er angivet nedenfor. Indtast den venligst i " "dit åbne browservindue." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Adgangskode-nulstillingskode" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Du modtager denne email, fordi du eller en anden har anmodet om nulstilling " "af adgangskoden til din brugerkonto.\n" "Den kan ignoreres, hvis du ikke har anmodet om nulstilling af adgangskode. " "Klik på linket nedenfor for at nulstille din adgangskode." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "I tilfælde af at du har glemt det er dit brugernavn %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Nulstilling af Password Email" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Din adgangskode er blevet nulstillet." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Din adgangskode er blevet indstillet." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Adgangskode indstillet" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Du modtager denne email, fordi du eller en anden har forsøgt at få adgang " "til en konto med emailen %(email)s. Vi har dog ingen registrering af sådan " "en konto i vores database." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Hvis det var dig, kan du tilmelde dig en konto ved hjælp af linket nedenfor." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Ukendt konto" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Emailadresse" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Nuværende email" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Ændrer til" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Din emailadresse afventer stadig bekræftelse." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Annuller ændring" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Skift til" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Skift email" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Bekræft venligst din emailadresse" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Bekræft venligst at %(email)s er en " "emailadresse for bruger %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Kan ikke bekræfte %(email)s, da den allerede er bekræftet af en anden konto." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Dette link til bekræftelse af email er udløbet eller ugyldigt. Lav venligst " "et nyt." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Hvis du ikke allerede har oprettet en konto, så %(link)sopret " "dig%(end_link)s først." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Log ind med en adgangsnøgle" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Send mig en login-kode" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Log ud" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Er du sikker på, at du vil logge af?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Du kan ikke fjerne din primære emailadresse (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Bekræftelses-email sendt til %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Du har bekræftet %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Fjernede emailadressen %(email)s." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Loggede succesfuldt ind med %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Du har logget ud." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "En login-kode er blevet sendt til %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Password ændret med success." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Password indstillet med success." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "En bekræftelseskode er blevet sendt til %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Du har bekræftet telefonnummer %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Primær emailadresse indstillet." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Ændr password" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Glemt password?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Glemt dit password? Skriv din emailadresse herunder, og vi vil sende dig en " "email, hvorfra du kan nulstille det." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Nulstil mit password" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Kontakt os venligst, hvis du har problemer med at nulstille dit password." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Vi har sendt en email til dig. Hvis du ikke har modtaget den, tjek venligst " "din spam-mappe. Ellers kontakt os, hvis du ikke modtager den inden for få " "minutter." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Ugyldigt token" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Linket til nulstilling af password var ugyldigt, muligvis fordi det allerede " "er blevet brugt. Lav venligst et nyt." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Dit password er nu ændret." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Indstil password" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Skift telefon" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Nuværende telefon" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Dit telefonnummer afventer stadig bekræftelse." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Skift telefon" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Indtast din adgangskode:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Du vil modtage en speciel kode til login uden adgangskode." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Anmod om kode" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Andre login-muligheder" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Opret" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Opret konto" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Har du allerede en konto? Så %(link)slog ind%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Tilmeld dig med en adgangsnøgle" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Tilmelding med adgangsnøgle" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Andre muligheder" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Lukket for nye konti" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Vi beklager, men der er for tiden lukket for oprettelse af nye konti." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Note" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Du er allerede logget ind som %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Advarsel:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Du har på nuværende tidspunkt ingen emailadresse tilknyttet. Du bør virkelig " "tilføje en emailadresse, så du kan modtage notifikationer, nulstille din " "adgangskode osv." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Bekræft din emailadresse" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Vi har sendt en email til dig til bekræftelse. Følg det angivne link for at " "færdiggøre tilmeldingsprocessen. Hvis du ikke ser bekræftelsesemailen i din " "indbakke, tjek din spam-mappe. Kontakt os venligst, hvis du ikke modtager " "bekræftelsesemailen inden for få minutter." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Denne del af siden kræver, at vi bekræfter, at\n" "du er hvem du påstår at være. Derfor kræver vi, at du\n" "bekræfter ejerskab af din emailadresse. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Vi har sendt en email til dig til\n" "bekræftelse. Klik venligst på linket i den email. Hvis du ikke ser " "bekræftelsesemailen i din indbakke, tjek din spam-mappe. Ellers\n" "kontakt os, hvis du ikke modtager den inden for få minutter." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Bemærk: du kan stadig ændre din " "emailadresse." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Beskeder:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menu:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Kontoforbindelser" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "To-faktor-autentificering" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sessioner" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Autoriser" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s ønsker at få adgang til din %(site_name)s konto." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Indtast enhedskode" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Indtast koden vist på din enhed." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Fortsæt" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Bekræft enhed" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Bekræft venligst koden vist på din %(client_name)s for at autorisere denne " "enhed." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Afvis" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Enhed autoriseret" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Du har autoriseret din %(client_name)s enhed." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Enhed afvist" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Autorisering af din %(client_name)s enhed er blevet afvist." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Fejl" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Forbliv logget ind" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Din konto er beskyttet af to-faktor-autentificering. Indtast venligst en " "autentificeringskode:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Et nyt sæt to-faktor-autentificering gendannelseskoder er blevet genereret." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Nye gendannelseskoder genereret" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Autentificeringsapp aktiveret." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Autentificeringsapp aktiveret" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Autentificeringsapp deaktiveret." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Autentificeringsapp deaktiveret" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "En ny sikkerhedsnøgle er blevet tilføjet." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Sikkerhedsnøgle tilføjet" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "En sikkerhedsnøgle er blevet fjernet." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Sikkerhedsnøgle fjernet" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Autentificeringsapp" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Autentificering via en autentificeringsapp er aktiv." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "En autentificeringsapp er ikke aktiv." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Deaktiver" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Aktiver" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Sikkerhedsnøgler" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Du har tilføjet %(count)s sikkerhedsnøgle." msgstr[1] "Du har tilføjet %(count)s sikkerhedsnøgler." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Ingen sikkerhedsnøgler er blevet tilføjet." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Administrer" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Tilføj" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Gendannelseskoder" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Der er %(unused_count)s ud af %(total_count)s gendannelseskoder tilgængelige." msgstr[1] "" "Der er %(unused_count)s ud af %(total_count)s gendannelseskoder tilgængelige." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Ingen gendannelseskoder er sat op." #: templates/mfa/index.html:96 msgid "View" msgstr "Vis" #: templates/mfa/index.html:102 msgid "Download" msgstr "Download" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Generer" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Et nyt sæt gendannelseskoder er blevet genereret." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Sikkerhedsnøgle tilføjet." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Sikkerhedsnøgle fjernet." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Indtast en autentificeringskode:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Du er ved at generere et nyt sæt gendannelseskoder til din konto." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Denne handling vil ugyldiggøre dine eksisterende koder." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Er du sikker?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Ubrugte koder" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Download koder" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Generer nye koder" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Aktiver autentificeringsapp" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "For at beskytte din konto med to-faktor-autentificering, skal du scanne QR-" "koden nedenfor med din autentificeringsapp. Indtast derefter " "bekræftelseskoden genereret af appen nedenfor." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Autentificeringshemmelighed" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Du kan gemme denne hemmelighed og bruge den til at geninstallere din " "autentificeringsapp på et senere tidspunkt." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Deaktiver autentificeringsapp" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Du er ved at deaktivere autentificeringsapp-baseret autentificering. Er du " "sikker?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Stol på denne browser?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Hvis du vælger at stole på denne browser, vil du ikke blive spurgt om en " "bekræftelseskode næste gang du logger ind." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Stol på i %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Stol ikke" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Tilføj sikkerhedsnøgle" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Fjern sikkerhedsnøgle" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Er du sikker på, at du vil fjerne denne sikkerhedsnøgle?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Anvendelse" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Adgangsnøgle" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Sikkerhedsnøgle" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Denne nøgle angiver ikke, om den er en adgangsnøgle." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Uspecificeret" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Tilføjet den %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Senest brugt %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Rediger" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Rediger sikkerhedsnøgle" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Gem" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Opret adgangsnøgle" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Du er ved at oprette en adgangsnøgle til din konto. Da du kan tilføje flere " "nøgler senere, kan du bruge et beskrivende navn til at skelne nøglerne fra " "hinanden." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Opret" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Denne funktionalitet kræver JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Tredjepartslogin-fejl" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Der opstod en fejl under forsøget på at logge ind via din tredjepartskonto." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Du kan logge ind på din konto ved hjælp af en af følgende tredjepartskonti:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Du har i øjeblikket ingen tredjepartskonti tilknyttet denne konto." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Tilføj en tredjepartskonto" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "" "En tredjepartskonto fra %(provider)s er blevet tilsluttet til din konto." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Tredjepartskonto tilsluttet" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "En tredjepartskonto fra %(provider)s er blevet frakoblet din konto." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Tredjepartskonto frakoblet" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Tilslut %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Du er ved at tilslutte en ny tredjepartskonto fra %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Log ind via %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "Du er ved at logge ind med en tredjepartskonto fra %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Log ind afbrudt" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Du valgte at afbryde log ind på vores side med en af dine eksisterende " "konti. Hvis det var en fejl, så fortsæt venligst til log ind." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Tredjepartskontoen er blevet tilsluttet." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Tredjepartskontoen er blevet frakoblet." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Du er ved at bruge din %(provider_name)s -konto til at logge ind i\n" "%(site_name)s. Som et sidste skridt, udfyld venligst denne formular:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Eller brug en tredjepart" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Logget ud af alle andre sessioner." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Startet kl." #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP-adresse" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Browser" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Sidst set kl." #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Nuværende" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Log ud af andre sessioner" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Brugersessioner" #: usersessions/models.py:94 msgid "session key" msgstr "sessionsnøgle" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Kontoforbindelser" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Adgangskoden skal være på mindst {0} tegn." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Hej fra %(site_name)s!\n" #~ "\n" #~ "Du har modtaget denne email fordi du eller en anden har bedt om et " #~ "password til din konto.\n" #~ "Den kan trygt ses bort fra, hvis du ikke har bedt om at få nulstillet dit " #~ "password. Klik linket herunder for at nulstille dit password." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "De følgende emailadresser er tilknyttet din konto:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Bekræft venligst din emailadresse" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Log venligst ind med en\n" #~ "af dine eksisterende tredjeparts konti. Eller, opret\n" #~ "en konto på %(site_name)s og log ind herunder:" #~ msgid "or" #~ msgstr "eller" #~ msgid "change password" #~ msgstr "ændr password" #~ msgid "OpenID Sign In" #~ msgstr "OpenID Log ind" #~ msgid "This email address is already associated with another account." #~ msgstr "Denne emailadresse er allerede knyttet til en anden konto." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Vi har sendt dig en email. Kontakt os venligst, hvis du ikke modtager den " #~ "i løbet af et par minutter." ================================================ FILE: allauth/locale/de/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Jannis Vajen, 2013-2014 msgid "" msgstr "" "Project-Id-Version: django-allauth\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-02-13 09:05-0600\n" "PO-Revision-Date: 2025-10-31 09:40+0000\n" "Last-Translator: sowinski \n" "Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.14.1-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Dieses Konto ist derzeit inaktiv." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Du kannst deine primäre E-Mail-Adresse nicht löschen." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Diese E-Mail-Adresse wird bereits in diesem Konto verwendet." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Die E-Mail-Adresse und/oder das Passwort sind leider falsch." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "" "Die angegebene Telefonnummer und/oder das Passwort sind leider falsch." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Es ist bereits jemand mit dieser E-Mail-Adresse registriert." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Bitte gib dein aktuelles Passwort ein." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Falscher Code." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Falsches Passwort." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Falsches oder abgelaufenes Token." #: account/adapter.py:79 msgid "Invalid login." msgstr "Ungültige Anmeldung." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Das Sicherheits-Token zum Zurücksetzen des Passwortes war ungültig." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Du kannst nicht mehr als %d E-Mail-Adressen hinzufügen." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Es ist bereits ein Benutzer mit dieser Rufnummer registriert." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "" "Zu viele gescheiterte Anmeldeversuche. Bitte versuche es später erneut." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Diese E-Mail-Adresse ist keinem Konto zugeordnet." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Die Telefonnummer ist keinem Benutzerkonto zugeordnet." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Deine primäre E-Mailadresse muss bestätigt werden." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "" "Anmeldename kann nicht verwendet werden. Bitte wähle einen anderen Namen." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Der Anmeldename und/oder das Passwort sind leider falsch." #: account/adapter.py:98 msgid "Please select only one." msgstr "Bitte wähle nur eine aus." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Die neue Wert muss sich vom aktuellen unterscheiden." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Bitte habe Geduld, du sendest zu viele Anfragen." #: account/adapter.py:826 msgid "Use your password" msgstr "Verwenden Sie Ihr Passwort" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Verwenden Sie eine Authentifizierungs-App oder einen Code" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Verwenden Sie einen Sicherheitsschlüssel" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} als verifiziert markieren." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Fehler beim Markieren von {email} als verifiziert." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Markierte E-Mail-Adressen als verifiziert kennzeichnen" #: account/apps.py:11 msgid "Accounts" msgstr "Konten" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-Mail" #: account/fields.py:19 msgid "Email address" msgstr "E-Mail-Adresse" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" "Geben Sie eine Telefonnummer einschließlich Ländervorwahl ein (z. B. +49 für" " Deutschland)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefon" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Du musst zweimal das selbe Passwort eingeben." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Passwort" #: account/forms.py:67 msgid "Remember Me" msgstr "Angemeldet bleiben" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Anmeldename" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Anmeldung" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Anmeldename, E-Mail oder Telefon" #: account/forms.py:117 msgid "Username or email" msgstr "Anmeldename oder E-Mail" #: account/forms.py:119 msgid "Username or phone" msgstr "Anmeldename oder Telefon" #: account/forms.py:121 msgid "Email or phone" msgstr "E-Mail oder Telefon" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Passwort vergessen?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-Mail (wiederholen)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Bestätigung der E-Mail-Adresse" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-Mail (optional)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Anmeldename (optional)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Du musst zweimal dieselbe E-Mail-Adresse eingeben." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Passwort (Wiederholung)" #: account/forms.py:591 msgid "Current Password" msgstr "Aktuelles Passwort" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Neues Passwort" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Neues Passwort (Wiederholung)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Code" #: account/models.py:23 msgid "user" msgstr "Benutzer" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "E-Mail-Adresse" #: account/models.py:31 msgid "verified" msgstr "bestätigt" #: account/models.py:32 msgid "primary" msgstr "Primär" #: account/models.py:38 msgid "email addresses" msgstr "E-Mail-Adressen" #: account/models.py:142 msgid "created" msgstr "Erstellt" #: account/models.py:143 msgid "sent" msgstr "Gesendet" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "Schlüssel" #: account/models.py:149 msgid "email confirmation" msgstr "E-Mail-Bestätigung" #: account/models.py:150 msgid "email confirmations" msgstr "E-Mail-Bestätigungen" #: headless/apps.py:7 msgid "Headless" msgstr "Kopflos" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Deine Benutzer-ID ansehen" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Deine E-Mail-Adresse ansehen" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Deine grundlegenden Profilinformationen ansehen" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Berechtigungen erteilen" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Platzhalter sind nicht erlaubt, es sei denn, 'URI-Platzhalter zulassen' ist " "aktiviert." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' enthält mehr als einen Platzhalter (*). Pro URI ist nur ein " "Platzhalter erlaubt." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Platzhalter sind nur im Hostnamen-Teil der URI erlaubt." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Autorisierungscode" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Gerätecode" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Client-Anmeldedaten" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Token auffrischen" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Vertraulich" #: idp/oidc/models.py:44 msgid "Public" msgstr "Öffentlich" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Die Geltungsbereiche, die der Client anfordern darf. Gib einen Wert pro " "Zeile an, z.B.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Falls der Client keinen Geltungsbereich angibt, werden diese Standard-" "Geltungsbereiche verwendet. Gib einen Wert pro Zeile an, z.B.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Eine Liste erlaubter Grant-Typen. Gib einen Wert pro Zeile an, z.B.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Eine Liste erlaubter Ursprünge für Cross-Origin-Anfragen, eine pro Zeile." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Platzhalter (*) in Weiterleitungs-URIs und CORS-Ursprüngen zulassen. Wenn " "aktiviert, können URIs ein einzelnes Sternchen enthalten, um Subdomains " "abzugleichen." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Eine Liste erlaubter Antworttypen. Gib einen Wert pro Zeile an, z.B.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "Client" #: idp/oidc/models.py:116 msgid "clients" msgstr "Clients" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Sie können keine E-Mail-Adresse zu einem Konto hinzufügen, das durch die " "Zwei-Faktor-Authentifizierung geschützt ist." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Sie können die Zwei-Faktor-Authentifizierung nicht deaktivieren." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Sie können keine Wiederherstellungscodes generieren, ohne die Zwei-Faktor-" "Authentifizierung aktiviert zu haben." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Sie können die Zwei-Faktor-Authentifizierung nicht aktivieren, bis Sie Ihre " "E-Mail-Adresse verifiziert haben." #: mfa/adapter.py:141 msgid "Master key" msgstr "Hauptschlüssel" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Sicherungsschlüssel" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Schlüsselnr. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Wiederherstellungs-Codes" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP Authenticator" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Authentifizierungscode" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Passwortlos" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Wenn Sie den passwortlosen Betrieb aktivieren, können Sie sich nur mit " "diesem Schlüssel anmelden, müssen aber zusätzliche Anforderungen erfüllen, " "wie z. B. biometrische Daten oder PIN-Schutz." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Es existiert bereits ein Konto mit dieser E-Mail-Adresse. Bitte melde dich " "zuerst mit diesem Konto an, und verknüpfe es dann mit deinem %s-Konto." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Falsches Token." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Für dein Konto wurde noch kein Passwort festgelegt." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Dein Konto hat keine bestätigte E-Mail-Adresse." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "" "Sie können Ihr letztes verbleibendes Drittanbieterkonto nicht trennen." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "" "Das Konto des Drittanbieters ist bereits mit einem anderen Konto dieser " "Seite verknüpft." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Konto" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "Anbieter" #: socialaccount/models.py:53 msgid "provider ID" msgstr "Anbieter-ID" #: socialaccount/models.py:57 msgid "name" msgstr "Name" #: socialaccount/models.py:59 msgid "client id" msgstr "Client-ID" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "App-ID oder 'Consumer key'" #: socialaccount/models.py:64 msgid "secret key" msgstr "Geheimer Schlüssel" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "'API secret', 'client secret' oder 'consumer secret'" #: socialaccount/models.py:70 #: templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Schlüssel" #: socialaccount/models.py:82 msgid "social application" msgstr "Soziale Anwendung" #: socialaccount/models.py:83 msgid "social applications" msgstr "Soziale Anwendungen" #: socialaccount/models.py:118 msgid "uid" msgstr "UID" #: socialaccount/models.py:120 msgid "last login" msgstr "Letzte Anmeldung" #: socialaccount/models.py:121 msgid "date joined" msgstr "Registrierdatum" #: socialaccount/models.py:122 msgid "extra data" msgstr "Weitere Daten" #: socialaccount/models.py:126 msgid "social account" msgstr "Soziales Konto" #: socialaccount/models.py:127 msgid "social accounts" msgstr "Soziale Konten" #: socialaccount/models.py:161 msgid "token" msgstr "Token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) oder \"access token\" (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "Geheimes Token" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) oder \"refresh token\" (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "Läuft ab" #: socialaccount/models.py:175 msgid "social application token" msgstr "Token für soziale Anwendung" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "Tokens für soziale Anwendungen" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Ungültige Profildaten" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Anmeldung" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Abbrechen" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: " "%s." msgstr "" "Ungültige Antwort von \"%s\" als Anfrageschlüssel erbeten wurde. Die Antwort" " war: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Ungültige Antwort von \"%s\" als Zugangsschlüssel erbeten wurde." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Kein Request-Token gespeichert für \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Kein Access-Token gespeichert für \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Kein Zugriff zu privaten Daten auf \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Ungültige Antwort von \"%s\" als Anfrageschlüssel erbeten wurde." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Konto inaktiv" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Dieses Konto ist inaktiv." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Wir haben einen Code an %(recipient)s gesendet. Der Code läuft in Kürze ab, " "bitte geben Sie ihn bald ein." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Bestätigen" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Neuen Code anfordern" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Zugriff bestätigen" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Bitte authentifizieren Sie sich erneut, um Ihr Konto zu schützen." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Alternative Optionen" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "E-Mail-Verifizierung" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Geben Sie den E-Mail-Bestätigungscode ein" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Andere E-Mail-Adresse verwenden" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Anmeldung" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Anmeldecode eingeben" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Passwort zurücksetzen" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Passwort-Zurücksetzungscode Eingeben" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Telefonverifizierung" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Geben Sie den Telefon-Bestätigungscode ein" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Eine andere Telefonnummer verwenden" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "E-Mail-Adressen" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Folgende E-Mail-Adressen sind mit diesem Konto verknüpft:" #: templates/account/email.html:25 msgid "Verified" msgstr "Bestätigt" #: templates/account/email.html:29 msgid "Unverified" msgstr "Unbestätigt" #: templates/account/email.html:34 msgid "Primary" msgstr "Primär" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Als primäre Adresse festlegen" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Bestätigungs-Mail nochmal verschicken" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Ausgewählte entfernen" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "E-Mail-Adresse hinzufügen" #: templates/account/email.html:70 msgid "Add Email" msgstr "E-Mail hinzufügen" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Möchtest du wirklich die ausgewählte E-Mail-Adresse entfernen?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you have\n" "forgotten about this, please use the password forgotten procedure to recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Du erhältst diese E-Mail, weil du oder jemand anderes versucht hat, sich für ein Konto anzumelden mit E-Mail-Adresse:\n" "\n" "%(email)s\n" "\n" "Es existiert jedoch bereits ein Konto mit dieser E-Mail-Adresse. Falls du dies vergessen hast, verwende bitte das Passwort-Vergessen-Verfahren, um dein Konto wiederherzustellen:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Konto existiert bereits" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Hallo von %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Danke dass du %(site_name)s nutzt!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Sie erhalten diese E-Mail, weil die folgende Änderung an Ihrem Konto " "vorgenommen wurde:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Wenn Sie diese Änderung nicht erkennen, ergreifen Sie bitte umgehend angemessene Sicherheitsmaßnahmen. Die Änderung an Ihrem Konto stammt von:\n" "\n" "- IP-Adresse: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Datum: %(timestamp)s\"" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Ihre E-Mail wurde von %(from_email)s auf %(to_email)s geändert." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "E-Mail geändert" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Ihre E-Mail wurde bestätigt." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "E-Mail-Bestätigung" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Sie erhalten diese E-Mail, weil Benutzer %(user_display)s Ihre E-Mail-" "Adresse angegeben hat, um ein Konto bei %(site_domain)s zu registrieren." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Ihr E-Mail-Verifizierungscode ist unten aufgeführt. Bitte geben Sie diese in" " Ihrem geöffneten Browserfenster ein." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "" "Um zu bestätigen, dass dies korrekt ist, gehen Sie zu %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Bitte bestätige deine E-Mail-Adresse" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "Die E-Mail-Adresse %(deleted_email)s wurde aus Ihrem Konto entfernt." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "E-Mail entfernt" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Ihr Anmeldecode steht unten. Bitte geben Sie ihn in Ihrem geöffneten " "Browserfenster ein." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Diese E-Mail kann ignoriert werden, wenn Sie diese Aktion nicht initiiert " "haben." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Anmeldecode" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Dein Passwort wurde geändert." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Passwort geändert" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Ihre Passwort-Zurücksetzungscode steht unten. Bitte geben Sie ihn in Ihrem " "geöffneten Browserfenster ein." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Passwort-Zurücksetzungscode" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the link below to reset your password." msgstr "" "Du erhältst diese E-Mail weil du oder jemand anderes die Zurücksetzung des Passwortes für dein Konto gefordert hat.\n" "Falls es sich dabei nicht um dich handelt, kann diese Nachricht ignoriert werden. Rufe folgende Adresse auf um dein Passwort zurückzusetzen." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "" "Falls du deinen Anmeldenamen vergessen haben solltest; er lautet " "%(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "E-Mail zum Zurücksetzen des Passworts" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Ihr Passwort wurde zurückgesetzt." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Ihr Passwort wurde festgelegt." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Passwort festgelegt" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Sie erhalten diese E-Mail, weil Sie oder jemand anderes versucht hat, auf " "ein Konto mit der E-Mail %(email)s zuzugreifen. Wir haben jedoch keinen " "solchen Account in unserer Datenbank gefunden." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Wenn es von Ihnen war, können Sie über den unten stehenden Link ein Konto " "erstellen." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Unbekanntes Konto" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Email-Adresse" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Aktuelle E-Mail" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Wechsel zu" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Deine E-Mail-Adresse steht immer noch aus und muss überprüft werden." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Änderung abbrechen" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Ändern zu" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Email ändern" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "E-Mail-Adresse bestätigen" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Bitte bestätige, dass %(email)s eine " "E-Mail-Adresse von %(user_display)s ist." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Kann %(email)s nicht bestätigen, da sie bereits von einem anderen Konto " "bestätigt wurde." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Dieser Bestätigungslink ist leider abgelaufen. Lass Dir bitte eine neue Bestätigungs-Mail schicken." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Du hast noch kein Konto bei uns? Dann %(link)serstelle%(end_link)s bitte " "zunächst eins." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Mit einem Passkey anmelden" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Anmeldecode senden" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Abmelden" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Bist du sicher, dass du dich abmelden möchtest?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Du kannst deine primäre E-Mail-Adresse (%(email)s) nicht löschen." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Bestätigungs-E-Mail wurde an %(email)s verschickt." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Du hast die Adresse %(email)s bestätigt." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "E-Mailadresse %(email)s entfernt." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Erfolgreich als %(name)s angemeldet." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Du hast dich abgemeldet." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Ein Anmeldecode wurde an %(recipient)s gesendet." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Das Passwort wurde geändert." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Das Passwort wurde erfolgreich gesetzt." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Ein Verifizierungscode wurde an %(phone)s gesendet." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Sie haben die Telefonnummer %(phone)s verifiziert." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Primäre E-Mailadresse festgelegt." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Passwort ändern" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Passwort vergessen?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Passwort vergessen? Gib deine E-Mail-Adresse unten ein, dann schicken wir " "dir einen Link, unter dem du dein Passwort zurücksetzen kannst." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Passwort zurücksetzen" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Bitte kontaktiere uns, wenn das Zurücksetzen des Passworts nicht klappt." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Wir haben Dir eine E-Mail geschickt. Wenn du die E-Mail nicht in deinem " "Posteingang siehst, überprüfe bitte deinen Spam-Ordner. Wenn die E-Mail " "ansonsten nicht in ein paar Minuten angekommen ist, gib uns bitte Bescheid." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Falsches Token" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password " "reset." msgstr "" "Der Link zum Zurücksetzen des Passworts war ungültig, womöglich wurde dieser" " Link bereits benutzt. Bitte lass dein Passwort noch mal zurücksetzen." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Dein Passwort wurde geändert." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Passwort setzen" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Telefon ändern" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Aktuelle Telefonnummer" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Dein Telefonnummer steht immer noch aus und muss überprüft werden." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Telefon ändern" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Geben Sie Ihr Passwort ein:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Sie erhalten einen speziellen Code für eine passwortfreie Anmeldung." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Code anfordern" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Andere Anmeldeoptionen" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Registrieren" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 #: templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Registrieren" #: templates/account/signup.html:17 #: templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "" "Du hast bereits ein Konto bei uns? Dann bitte %(link)shier " "entlang%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Mit einem Passkey registrieren" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Passkey Registrierung" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Andere Optionen" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Registrierung geschlossen" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Es tut uns leid, aber die Registrierung ist derzeit geschlossen." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Anmerkung" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Du bist bereits als %(user_display)s angemeldet." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Warnung:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an" " email address so you can receive notifications, reset your password, etc." msgstr "" "Du hast derzeit keine E-Mail-Adressen angegeben. Das solltest du allerdings " "tun, denn nur so können wir dich benachrichtigen und dein Passwort " "zurücksetzen." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Bestätige deine E-Mail-Adresse" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Wir haben dir eine E-Mail geschickt, um deine Adresse zu verifizieren. Bitte" " folge dem Link in der E-Mail um den Anmeldeprozess abzuschließen. Wenn du " "die E-Mail nicht in deinem Posteingang siehst, überprüfe bitte deinen Spam-" "Ordner. Wenn die E-Mail nicht in ein paar Minuten angekommen ist, gib uns " "bitte Bescheid." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Auf diesem Teil der Webseite möchten wie sichergehen,\n" "dass du die Person bist für die du dich ausgibst.\n" "Dazu musst du deine E-Mail-Adresse verifizieren. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see the verification email in your main inbox, check your spam folder. Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Wir haben Dir eine E-Mail geschickt, um deine\n" "Adresse zu verifizieren. Bitte klick auf den Link\n" "in der E-Mail. Wenn du die E-Mail nicht in deinem Posteingang siehst, überprüfe bitte deinen Spam-Ordner. Wenn die E-Mail ansonsten nicht in ein paar Minuten angekommen ist, gib uns bitte Bescheid." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Anmerkung: Du kannst Deine " "E-Mail-Adresse ändern." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Nachrichten:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menü" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Konto-Verknüpfungen" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Zwei-Faktor-Authentifizierung" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sitzungen" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Autorisieren" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s möchte auf dein %(site_name)s Konto zugreifen." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Gerätecode eingeben" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Gib den auf deinem Gerät angezeigten Code ein." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Fortfahren" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Gerät bestätigen" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Bitte bestätige den auf deinem %(client_name)s angezeigten Code, um dieses " "Gerät zu autorisieren." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Ablehnen" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Gerät autorisiert" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Du hast dein %(client_name)s Gerät erfolgreich autorisiert." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Gerät abgelehnt" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Die Autorisierung für dein %(client_name)s Gerät wurde abgelehnt." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Fehler" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Angemeldet bleiben" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Ihr Konto ist durch Zwei-Faktor-Authentifizierung geschützt. Bitte geben Sie" " einen Authenticator-Code ein:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "Ein neuer Satz Wiederherstellungscodes wurde generiert." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Neue Wiederherstellungscodes generiert" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Authenticator-App aktiviert." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Authenticator-App aktiviert" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Authenticator-App deaktiviert." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Authenticator-App deaktiviert" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Es wurde ein neuer Sicherheitsschlüssel hinzugefügt." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Sicherheitsschlüssel hinzugefügt" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Ein Sicherheitsschlüssel wurde entfernt." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Sicherheitsschlüssel entfernt" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Authenticator-App" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Die Authentifizierung mit einer Authenticator-App ist aktiv." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Es ist keine Authenticator-App aktiv." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Deaktivieren" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Aktivieren" #: templates/mfa/index.html:45 #: templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Sicherheitsschlüssel" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Sie haben %(count)s Sicherheitsschlüssel hinzugefügt." msgstr[1] "Sie haben %(count)s Sicherheitsschlüssel hinzugefügt." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Es wurden keine Sicherheitsschlüssel hinzugefügt." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Verwalten" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Hinzufügen" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Wiederherstellungs-Codes" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Es steht %(unused_count)s von %(total_count)s Wiederherstellungscodes zur " "Verfügung." msgstr[1] "" "Es stehen %(unused_count)s von %(total_count)s Wiederherstellungscodes zur " "Verfügung." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Es wurden keine Wiederherstellungscodes eingerichtet." #: templates/mfa/index.html:96 msgid "View" msgstr "Anzeigen" #: templates/mfa/index.html:102 msgid "Download" msgstr "Herunterladen" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Generieren" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Ein neuer Satz Wiederherstellungscodes wurde generiert." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Sicherheitsschlüssel hinzugefügt." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Sicherheitsschlüssel entfernt." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Geben Sie einen Authentifizierungscode ein:" #: templates/mfa/recovery_codes/generate.html:9 msgid "" "You are about to generate a new set of recovery codes for your account." msgstr "" "Du bist dabei, einen neuen Satz Wiederherstellungscodes für Ihr Konto zu " "generieren." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Diese Aktion wird Ihre vorhandenen Codes ungültig machen." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Sind Sie sicher?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Unbenutzte Codes" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Codes herunterladen" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Neue Codes generieren" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Authenticator-App aktivieren" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Um Ihr Konto mit der Zwei-Faktor-Authentifizierung zu schützen, scannen Sie " "den unten stehenden QR-Code mit Ihrer Authenticator-App. Geben Sie dann den " "von der App generierten Bestätigungscode unten ein." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Authentifizierungsgeheimnis" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Sie können dieses Geheimnis speichern und zu einem späteren Zeitpunkt " "verwenden, um Ihre Authenticator-App neu zu installieren." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Authenticator-App deaktivieren" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Du bist dabei, die Authentifizierung per Authenticator-App zu deaktivieren. " "Sind Sie sicher?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Diesem Browser vertrauen?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Wenn Sie diesem Browser vertrauen, brauchen Sie beim nächsten Anmelden " "keinen Bestätigungscode." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Für %(period)s vertrauen" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Nicht vertrauen" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Sicherheitsschlüssel hinzufügen" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Sicherheitsschlüssel entfernen" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "" "Sind Sie sicher, dass Sie diesen Sicherheitsschlüssel entfernen möchten?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Verwendung" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Passkey" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Sicherheitsschlüssel" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Dieser Schlüssel gibt nicht an, ob es sich um einen Passkey handelt." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Unbestimmt" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Hinzugefügt um %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Zuletzt verwendet %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Bearbeiten" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Sicherheitsschlüssel bearbeiten" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Speichern" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Passkey erstellen" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Sie sind dabei, einen Passkey für Ihr Konto zu erstellen. Da Sie später " "weitere Schlüssel hinzufügen können, können Sie einen beschreibenden Namen " "verwenden, um die Schlüssel voneinander zu unterscheiden." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Erstellen" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Diese Funktionalität erfordert JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Drittanbieter-Anmeldefehler" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Beim Versuch, sich über Ihr Drittanbieterkonto anzumelden, ist ein Fehler " "aufgetreten." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Sie können sich bei Ihrem Konto mit einem der folgenden Drittanbieter-Konten" " anmelden:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Derzeit sind keine Drittanbieterkonten mit diesem Konto verbunden." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Einen Drittanbieter-Account hinzufügen" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "" "Ein Drittanbieterkonto von %(provider)s wurde mit Ihrem Konto verbunden." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Drittanbieterkonto verbunden" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" "Ein Drittanbieterkonto von %(provider)s wurde von Ihrem Konto getrennt." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Drittanbieterkonto getrennt" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Mit %(provider)s verbinden" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "" "Du bist dabei, ein neues Drittanbieter-Konto von %(provider)s zu verknüpfen." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Anmelden über %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "" "You are about to sign in using a third-party account from %(provider)s." msgstr "Du bist dabei, dich mit einem Konto von %(provider)s anzumelden." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Anmeldung abgebrochen" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Du hast die Anmeldung abgebrochen. Wenn das nur ein Versehen oder ein Fehler" " war, folge bitte diesem Link um dich " "anzumelden." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Konten wurden erfolgreich verknüpft." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Das Drittanbieterkonto wurde getrennt." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Du verwendest dein %(provider_name)s-Konto, um dich bei\n" "%(site_name)s anzumelden. Zum Abschluss bitte das folgende Formular ausfüllen:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Oder Drittanbieter verwenden" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Von allen anderen Sitzungen abgemeldet." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Gestartet um" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP-Adresse" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Browser" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Zuletzt gesehen um" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Aktuell" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Andere Sitzungen abmelden" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Benutzersitzungen" #: usersessions/models.py:94 msgid "session key" msgstr "Sitzungsschlüssel" #~ msgid "Account Connection" #~ msgstr "Konto-Verknüpfung" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Das Passwort muss aus mindestens {0} Zeichen bestehen." #, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Du erhältst diese E-Mail weil du oder jemand anderes die Zurücksetzung des Passwortes für dein Konto gefordert hat. Wir haben jedoch keinen Eintrag eines Benutzers mit der E-Mail-Adresse %(email)s in unserer Datenbank.\n" #~ "\n" #~ "Falls es sich dabei nicht um dich handelt, kann diese Nachricht ignoriert werden.\n" #~ "\n" #~ "Ansonsten kannst du dich über den unten stehenden Link für ein Konto anmelden." #~ msgid "The following email address is associated with your account:" #~ msgstr "Die folgende Email-Adresse ist mit Ihrem Konto verknüpft:" #~ msgid "Change Email Address" #~ msgstr "E-Mail-Adresse ändern" #~ msgid "To safeguard the security of your account, please enter your password:" #~ msgstr "" #~ "Um die Sicherheit Ihres Kontos zu gewährleisten, geben Sie bitte Ihr " #~ "Passwort ein:" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Bitte melde dich mit einem der folgenden Netzwerkkonten an, oder registriere dich auf %(site_name)s, dann kannst du dich unten mit deinem Konto anmelden:" #~ msgid "or" #~ msgstr "oder" #~ msgid "change password" #~ msgstr "Passwort ändern" #~ msgid "OpenID Sign In" #~ msgstr "OpenID-Anmeldung" #~ msgid "This email address is already associated with another account." #~ msgstr "Diese E-Mail-Adresse wird bereits in einem anderen Konto verwendet." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Wir haben Dir eine E-Mail geschickt. Bitte kontaktiere uns, wenn du sie " #~ "nicht in ein paar Minuten erhalten hast." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "Die Anmeldedaten sind leider falsch." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "" #~ "Anmeldenamen dürfen nur Buchstaben und Ziffern und folgende Zeichen " #~ "enthalten: @/./+/-/_." #~ msgid "This username is already taken. Please choose another." #~ msgstr "Der Anmeldename ist bereits vergeben – bitte wähle einen anderen." #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "Du hast bestätigt, dass %(email)s eine " #~ "gültige Adresse von %(user_display)s ist." #~ msgid "Thanks for using our site!" #~ msgstr "Danke, dass du unsere Seite nutzt!" ================================================ FILE: allauth/locale/el/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-07-25 20:24+0200\n" "Last-Translator: Antonis Christofides \n" "Language-Team: Greek \n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.13-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Αυτός ο λογαριασμός είναι ανενεργός." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "" "Δεν μπορείτε να καταργήσετε την κύρια διεύθυνση ηλεκτρονικού ταχυδρομείου " "σας." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Αυτό το e-mail χρησιμοποιείται ήδη από αυτό το λογαριασμό." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Η διέυθυνση e-mail ή/και το συνθηματικό που δόθηκαν δεν είναι σωστά." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "" "Ο αριθμός τηλεφώνου ή/και ο κωδικός πρόσβασης που δηλώσατε δεν είναι σωστοί." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Υπάρχει ήδη εγγεγραμμένος χρήστης με αυτό το e-mail." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Παρακαλώ γράψτε το τρέχον συνθηματικό σας." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Λανθασμένος κωδικός." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Λάθος κωδικός πρόσβασης." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Μη έγκυρο ή ληγμένο κλειδί." #: account/adapter.py:79 msgid "Invalid login." msgstr "Μη έγκυρη σύνδεση." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Το κουπόνι επαναφοράς του συνθηματικού δεν ήταν έγκυρο." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Δεν μπορείτε να προσθέσετε περισσότερες από %d διευθύνσεις email." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Υπάρχει ήδη εγγεγραμμένος χρήστης με αυτό τον αριθμό τηλεφώνου." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Πολλές αποτυχημένες προσπάθειες σύνδεσης. Προσπαθήστε ξανά αργότερα." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "" "Η διεύθυνση ηλεκτρονικού ταχυδρομείου δεν αποδίδεται σε κανένα λογαριασμό " "χρήστη." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Ο αριθμός τηλεφώνου δεν έχει εκχωρηθεί σε κανένα λογαριασμό χρήστη." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Η πρωτεύον διεύθυνση e-mail πρέπει να επιβεβαιωθεί." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Δεν μπορεί να χρησιμοποιηθεί αυτό το όνομα χρήστη. Δοκιμάστε άλλο." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Το όνομα χρήστη ή/και το συνθηματικό που δόθηκαν δεν είναι σωστά." #: account/adapter.py:98 msgid "Please select only one." msgstr "Παρακαλώ επιλέξτε μόνο ένα." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Η νέα τιμή πρέπει να είναι διαφορετική από την τρέχουσα." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Παρακαλώ περιμένετε, στέλνετε πάρα πολλά αιτήματα." #: account/adapter.py:826 msgid "Use your password" msgstr "Χρησιμοποιήστε τον κωδικό πρόσβασής σας" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Χρήση εφαρμογής ή κωδικού πιστοποίησης ταυτότητας" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Χρήση κλειδιού ασφαλείας" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "Η διεύθυνση {email} σημειώθηκε ως επαληθευμένη." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Αποτυχία σήμανσης του {email} ως επαληθευμένου." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Σημείωση επιλεγμένων διευθύνσεων email όπως επαληθεύτηκε" #: account/apps.py:11 msgid "Accounts" msgstr "Λογαριασμοί" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-mail" #: account/fields.py:19 msgid "Email address" msgstr "Διεύθυνση e-mail" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" "Εισάγετε έναν αριθμό τηλεφώνου, συμπεριλαμβανομένου του κωδικού χώρας (π.χ. " "+1 για τις ΗΠΑ)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Τηλέφωνο" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Πρέπει να δοθεί το ίδιο συνθηματικό." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Συνθηματικό" #: account/forms.py:67 msgid "Remember Me" msgstr "Αυτόματη Σύνδεση" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Όνομα χρήστη" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Σύνδεση" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Όνομα χρήστη, email ή τηλέφωνο" #: account/forms.py:117 msgid "Username or email" msgstr "Όνομα χρήστη ή e-mail" #: account/forms.py:119 msgid "Username or phone" msgstr "Όνομα χρήστη ή τηλέφωνο" #: account/forms.py:121 msgid "Email or phone" msgstr "Ηλεκτρονικό ταχυδρομείο ή τηλέφωνο" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Ξεχάσατε το συνθηματικό σας;" #: account/forms.py:287 msgid "Email (again)" msgstr "Ηλεκτρονικό ταχυδρομείο (ξανά)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Επιβεβαίωση διεύθυνσης ηλεκτρονικού ταχυδρομείου" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-mail (προαιρετικό)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Όνομα χρήστη (προαιρετικό)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Πρέπει να πληκτρολογείτε το ίδιο email κάθε φορά." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Συνθηματικό (επιβεβαίωση)" #: account/forms.py:591 msgid "Current Password" msgstr "Τρέχον συνθηματικό" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Νέο συνθηματικό" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Νέο συνθηματικό (επιβεβαίωση)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Κωδικός" #: account/models.py:23 msgid "user" msgstr "χρήστης" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "διεύθυνση e-mail" #: account/models.py:31 msgid "verified" msgstr "επαληθευμένο" #: account/models.py:32 msgid "primary" msgstr "πρωτεύον" #: account/models.py:38 msgid "email addresses" msgstr "διευθύνσεις e-mail" #: account/models.py:142 msgid "created" msgstr "δημιουργήθηκε" #: account/models.py:143 msgid "sent" msgstr "απστάλει" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "κλειδί" #: account/models.py:149 msgid "email confirmation" msgstr "e-mail επιβεβαίωσης" #: account/models.py:150 msgid "email confirmations" msgstr "e-mail επιβεβαίωσης" #: headless/apps.py:7 msgid "Headless" msgstr "Ακέφαλος" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Προβολή του αναγνωριστικού χρήστη σας" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Προβολή της διεύθυνσης email σας" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Προβολή των βασικών πληροφοριών προφίλ σας" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Παραχώρηση δικαιωμάτων" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Οι χαρακτήρες μπαλαντέρ (*) δεν επιτρέπονται εκτός εάν είναι ενεργοποιημένη " "η επιλογή 'Επιτρέψτε χαρακτήρες μπαλαντέρ σε URI'." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "Το URI '{}' περιέχει περισσότερους από έναν χαρακτήρες μπαλαντέρ (*). " "Επιτρέπεται μόνο ένας χαρακτήρας μπαλαντέρ ανά URI." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "" "Οι χαρακτήρες μπαλαντέρ (*) επιτρέπονται μόνο στο τμήμα του ονόματος " "κεντρικού υπολογιστή του URI." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Κωδικός εξουσιοδότησης" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Κωδικός συσκευής" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Διαπιστευτήρια πελάτη" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Token ανανέωσης" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Εμπιστευτικό" #: idp/oidc/models.py:44 msgid "Public" msgstr "Δημόσιο" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Τα πεδία εφαρμογής (scopes) που ο πελάτης επιτρέπεται να ζητήσει. Δώστε μία " "τιμή ανά γραμμή, π.χ.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Σε περίπτωση που ο πελάτης δεν καθορίσει κάποιο πεδίο εφαρμογής, " "χρησιμοποιούνται αυτά τα προεπιλεγμένα. Δώστε μία τιμή ανά γραμμή, π.χ.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Μια λίστα επιτρεπόμενων τύπων εξουσιοδότησης. Δώστε μία τιμή ανά γραμμή, " "π.χ.: authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Μια λίστα επιτρεπόμενων πηγών για αιτήματα cross-origin, μία ανά γραμμή." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Επιτρέψτε χαρακτήρες μπαλαντέρ (*) σε URI ανακατεύθυνσης και πηγές CORS. " "Όταν είναι ενεργοποιημένο, τα URI μπορούν να περιέχουν έναν αστερίσκο για " "αντιστοίχιση υποτομέων." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Μια λίστα επιτρεπόμενων τύπων απόκρισης. Δώστε μία τιμή ανά γραμμή, π.χ.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "πελάτης" #: idp/oidc/models.py:116 msgid "clients" msgstr "πελάτες" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Δεν μπορείτε να προσθέσετε μια διεύθυνση ηλεκτρονικού ταχυδρομείου σε έναν " "λογαριασμό που προστατεύεται με έλεγχο ταυτότητας δύο παραγόντων." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Δεν μπορείτε να απενεργοποιήσετε τον έλεγχο ταυτότητας δύο παραγόντων." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Δεν μπορείτε να δημιουργήσετε κωδικούς ανάκτησης χωρίς να έχετε " "ενεργοποιήσει τον έλεγχο ταυτότητας δύο παραγόντων." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Δεν μπορείτε να ενεργοποιήσετε τον έλεγχο ταυτότητας δύο παραγόντων μέχρι να " "επαληθεύσετε τη διεύθυνση ηλεκτρονικού ταχυδρομείου σας." #: mfa/adapter.py:141 msgid "Master key" msgstr "Βασικό κλειδί" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Εφεδρικό κλειδί" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Αριθμός κλειδιού {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Κωδικοί ανάκτησης" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "Αυθεντικοποιητής TOTP" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Κωδικός πιστοποίησης" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Χωρίς κωδικό πρόσβασης" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Η ενεργοποίηση της λειτουργίας χωρίς κωδικό πρόσβασης σάς επιτρέπει να " "συνδεθείτε χρησιμοποιώντας μόνο αυτό το κλειδί, αλλά επιβάλλει πρόσθετες " "απαιτήσεις, όπως βιομετρικά στοιχεία ή προστασία με PIN." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Υπάρχει ήδη ένας λογαριασμός με αυτή τη διεύθυνση ηλεκτρονικού ταχυδρομείου. " "Συνδεθείτε πρώτα σε αυτόν το λογαριασμό και, στη συνέχεια, συνδέστε το " "λογαριασμό %s." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Μη έγκυρη ένδειξη." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Δεν έχει οριστεί συνθηματικό στον λογαριασμό σας." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Δεν έχει επιβεβαιωθεί κανένα e-mail του λογαριασμού σας." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Δεν μπορείτε να αποσυνδέσετε τον τελευταίο λογαριασμό τρίτων." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "" "Ο λογαριασμός κοινωνικών μέσων είναι ήδη συνδεδεμένος με διαφορετικό λΟ " "λογαριασμός τρίτου μέρους είναι ήδη συνδεδεμένος με διαφορετικό λογαριασμό." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Λογαριασμοί Κοινωνικών Μέσων" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "πάροχος" #: socialaccount/models.py:53 msgid "provider ID" msgstr "ID παρόχου" #: socialaccount/models.py:57 msgid "name" msgstr "όνομα" #: socialaccount/models.py:59 msgid "client id" msgstr "id πελάτη" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "App ID ή consumer key(κλειδί καταναλωτή)" #: socialaccount/models.py:64 msgid "secret key" msgstr "μυστικό κλειδί" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API secret, client secret, ή consumer secret" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Κλειδί" #: socialaccount/models.py:82 msgid "social application" msgstr "εφαρμογή κοινωνικών μέσων" #: socialaccount/models.py:83 msgid "social applications" msgstr "εφαρμογές κοινωνικών μέσων" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "τελευταία σύνδεση" #: socialaccount/models.py:121 msgid "date joined" msgstr "ημερομηνία εγγραφής" #: socialaccount/models.py:122 msgid "extra data" msgstr "έξτρα δεδομένα" #: socialaccount/models.py:126 msgid "social account" msgstr "λογαριασμός κοινωνικών μέσων" #: socialaccount/models.py:127 msgid "social accounts" msgstr "λογαριασμοί κοινωνικών μέσων" #: socialaccount/models.py:161 msgid "token" msgstr "κουπόνι" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) ή access token (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "διακριτικό μυστικό" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) ή refresh token (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "λήγει στις" #: socialaccount/models.py:175 msgid "social application token" msgstr "token (κουπόνι) εφαρμογής κοινωνικών μέσων" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "tokens (κουπόνια) εφαρμογής κοινωνικών μέσων" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Άκυρα δεδομένα προφίλ" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Σύνδεση" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Ακύρωση" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Μη έγκυρη απάντηση κατά τη λήψη του συμβόλου αίτησης από το «%s». Η απάντηση " "ήταν: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Μη-έγκυρη απάντηση κατά την απόκτηση κουπονιού πρόσβασης από \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Δεν υπάρχει αποθηκευμένο κουπόνι αιτήματος για \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Δεν υπάρχει αποθηκευμένο κουπόνι πρόσβασης για \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Αδύνατη πρόσβαση σε ιδιοτικούς πόρους στο \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Μη-έγκυρη απάντηση κατά την απόκτηση κουπονιού αιτήματος από \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Λογαριασμός Ανενεργός" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Αυτός ο λογαριασμός είναι ανενεργός." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Στείλαμε έναν κωδικό σε %(recipient)s). Ο κωδικός λήγει σύντομα, γι' αυτό " "παρακαλούμε εισάγετε τον σύντομα." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Επιβεβαίωση" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Ζητήστε νέο κωδικό" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Επιβεβαίωση πρόσβασης" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "" "Παρακαλείστε να επαληθεύσετε την ταυτότητά σας για να διασφαλίσετε το " "λογαριασμό σας." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Εναλλακτικές επιλογές" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Επαλήθευση email" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Εισάγετε τον κωδικό επαλήθευσης email" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Χρησιμοποιήστε άλλη διεύθυνση e-mail" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Είσοδος" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Πληκτρολογήστε τον κωδικό εισόδου" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Επαναφορά Συνθηματικού" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Εισάγετε τον κωδικό επαναφοράς κωδικού πρόσβασης" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Επαλήθευση τηλεφώνου" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Εισάγετε τον κωδικό επαλήθευσης τηλεφώνου" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Χρησιμοποιήστε άλλο αριθμό τηλεφώνου" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Διεύθυνση e-mail" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "" "Οι διευθύνσεις e-mail που ακολουθούν είναι συσχετισμένες με τον λογαριασμό " "σας:" #: templates/account/email.html:25 msgid "Verified" msgstr "Εγκεκριμένος" #: templates/account/email.html:29 msgid "Unverified" msgstr "Μη-επιβεβαιωμένο" #: templates/account/email.html:34 msgid "Primary" msgstr "Πρωτεύον" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Ορισμός ως Πρωτεύον" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Επανάληψη αποστολής Επαλήθευσης" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Αφαίρεση" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Προσθήκη διέυθυνσης e-mail" #: templates/account/email.html:70 msgid "Add Email" msgstr "Προσθήκη e-mail" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Θέλετε να αφαιρέσετε την επλεγμένη διεύθυνση e-mail?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Λαμβάνετε αυτό το email επειδή εσείς ή κάποιος άλλος επιχείρησε να\n" "δημιουργήσει λογαριασμό με αυτή τη διεύθυνση email:\n" "\n" "%(email)s\n" "\n" "Ωστόσο, υπάρχει ήδη λογαριασμός που χρησιμοποιεί αυτή τη διεύθυνση email.\n" "Μπορείτε να χρησιμοποιήσετε τη διαδικασία ξεχασμένου συνθηματικού για να\n" "ανακτήσετε το λογαριασμό σας:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Ο λογαριασμός υπάρχει ήδη" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Γεια σας από το %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Ευχαριστούμε που χρησιμοποιείτε το %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Λαμβάνετε αυτό το μήνυμα επειδή έγινε η ακόλουθη αλλαγή στο λογαριασμό σας:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Εάν δεν αναγνωρίζετε αυτή την αλλαγή, τότε παρακαλούμε λάβετε αμέσως τα " "κατάλληλα μέτρα ασφαλείας. Η αλλαγή στο λογαριασμό σας προέρχεται από:\n" "\n" "- Διεύθυνση IP: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Ημερομηνία: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Το email σας έχει αλλάξει από %(from_email)s σε %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Ηλεκτρονικό ταχυδρομείο άλλαξε" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Το email σας έχει επιβεβαιωθεί." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Επιβεβαίωση μέσω ηλεκτρονικού ταχυδρομείου" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Λαμβάνετε αυτό το μήνυμα ηλεκτρονικού ταχυδρομείου επειδή ο χρήστης " "%(user_display)s έδωσε τη διεύθυνση ηλεκτρονικού ταχυδρομείου σας για την " "εγγραφή ενός λογαριασμού στο %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Ο κωδικός επαλήθευσης του email σας αναφέρεται παρακάτω. Παρακαλούμε " "εισάγετε τον στο ανοιχτό παράθυρο του προγράμματος περιήγησης." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "" "Για να επιβεβαιώσετε ότι αυτό είναι σωστό, μεταβείτε στο %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Παρακαλούμε να επιβεβαιώσετε την διεύθυνση e-mail σας" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "" "Η διεύθυνση ηλεκτρονικού ταχυδρομείου %(deleted_email)s έχει αφαιρεθεί από " "το λογαριασμό σας." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Το ηλεκτρονικό ταχυδρομείο αφαιρέθηκε" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Ο κωδικός σύνδεσής σας παρατίθεται παρακάτω. Παρακαλούμε εισάγετε τον στο " "ανοιχτό παράθυρο του προγράμματος περιήγησης." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Αυτό το μήνυμα μπορείτε να το αγνοήσετε με ασφάλεια, αν δεν προκαλέσατε " "εσείς αυτή την ενέργεια." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Κωδικός εισόδου" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Ο κωδικός πρόσβασής σας έχει αλλάξει." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Ο κωδικός πρόσβασης άλλαξε" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Ο κωδικός επαναφοράς του κωδικού πρόσβασης παρατίθεται παρακάτω. Παρακαλούμε " "εισάγετε τον στο ανοιχτό παράθυρο του προγράμματος περιήγησης." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Επαναφορά κωδικού πρόσβασης" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Λαμβάνετε αυτό το email επειδή ζητήσατε επαναφορά συνθηματικού για το " "λογαριασμό σας.\n" "Αν αυτό δεν ισχύει, μπορείτε να αγνοήσετε αυτό το μήνυμα. Ακολουθήστε τον " "παρακάτω σύνδεσμο για να επαναφέρετε το συνθηματικό σας." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Το όνομα χρήστη σας είναι %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "E-mail επαναφοράς συνθηματικού" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Ο κωδικός πρόσβασής σας έχει επαναρυθμιστεί." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Ο κωδικός πρόσβασής σας έχει ρυθμιστεί." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Ρύθμιση κωδικού πρόσβασης" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Λαμβάνετε αυτό το email επειδή εσείς ή κάποιος άλλος προσπάθησε να αποκτήσει " "πρόσβαση σε έναν λογαριασμό με ηλεκτρονικό ταχυδρομείο %(email)s. Ωστόσο, " "δεν έχουμε καμία καταγραφή ενός τέτοιου λογαριασμού στη βάση δεδομένων μας." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Αν ήσασταν εσείς, μπορείτε να εγγραφείτε για λογαριασμό χρησιμοποιώντας τον " "παρακάτω σύνδεσμο." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Άγνωστος λογαριασμός" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Διεύθυνση ηλεκτρονικού ταχυδρομείου" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Τρέχον ηλεκτρονικό ταχυδρομείο" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Αλλάζοντας σε" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "" "Η διεύθυνση ηλεκτρονικού ταχυδρομείου σας εκκρεμεί ακόμη προς επαλήθευση." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Ακύρωση Αλλαγή" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Αλλαγή σε" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Αλλαγή ηλεκτρονικού ταχυδρομείου" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Επιβεβαίωση διεύθυνση e-mail" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Παρακαλούμε επιβεβαιώστε ότι το %(email)s " "είναι μια διεύθυνση ηλεκτρονικού ταχυδρομείου για τον χρήστη " "%(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Δεν είναι δυνατή η επιβεβαίωση του %(email)s επειδή έχει ήδη επιβεβαιωθεί " "από διαφορετικό λογαριασμό." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Αυτός ο σύνδεσμος επιβεβαίωσης email έληξε ή είναι άκυρος. Παρακαλούμε να εκδώσετε ένα νέο αίτημα επιβεβαίωσης μέσω email." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Εάν δεν έχετε δημιουργήσει ακόμη λογαριασμό, τότε παρακαλούμε πρώτα να " "%(link)sεγγραφείτε%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Συνδεθείτε με κλειδί πρόσβασης" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Στείλτε μου έναν κωδικό σύνδεσης" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Αποσύνδεση" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Σίγουρα θέλετε να αποσυνδεθείτε;" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Δεν μπορείτε να αφαιρέσετε τη βασική διεύθυνση email (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "E-mail επιβεβαίωσης στάλθηκε στο %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Έχετε επιβεβαιώσει το %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Αφαιρέθηκε η διεύθυνση e-mail %(email)s." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Επιτυχημένη σύνδεση ως %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Έχετε αποσυνδεθεί." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Ένας κωδικός εισόδου έχει σταλεί στον %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Το συνθηματικό τροποποιήθηκε επιτυχώς." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Συνθηματικό ορίστηκε επιτυχώς." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Ένας κωδικός επιβεβαίωσης έχει σταλεί στο %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Έχετε επαληθεύσει τον αριθμό τηλεφώνου %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Ορίστηκε η πρωτεύον διεύθυνση e-mail." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Αλλαγή συνθηματικού" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Ξεχάσατε τον κωδικό πρόσβασης;" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Ξεχάσατε τον κωδικό πρόσβασής σας; Εισάγετε τη διεύθυνση email σας παρακάτω " "και θα σας στείλουμε ένα email που θα σας επιτρέψει να τον επαναφέρετε." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Επαναφορά του συνθηματικού μου" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Παρακαλούμε επικοινωνήστε μαζί μας αν υπάρξει οποιοδήποτε πρόβλημα κατα την " "επαναφορά του συνθηματικού σας." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Σας στείλαμε ένα email. Εάν δεν το λάβατε, παρακαλούμε ελέγξτε το φάκελο " "spam σας. Διαφορετικά, επικοινωνήστε μαζί μας εάν δεν το λάβετε σε λίγα " "λεπτά." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Λανθασμένο αναγνωριστικό" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Ο σύνδεσμος επαναφοράς συνθηματικού δεν ήταν έγκυρος, πιθανόν να έχει ήδη " "χρησιμοποιηθεί. Παρακαλούμε ζητήστε εκ νέου επαναφορά συνθηματικού." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Το συνθηματικό σας έχει αλλάξει." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Ορισμός Συνθηματικού" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Αλλαγή τηλεφώνου" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Τρέχον τηλέφωνο" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Ο αριθμός τηλεφώνου σας εκκρεμεί ακόμη προς επαλήθευση." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Αλλαγή τηλεφώνου" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Εισάγετε τον κωδικό πρόσβασής σας:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Θα λάβετε έναν ειδικό κωδικό για είσοδο χωρίς κωδικό πρόσβασης." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Ζητήστε κωδικός" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Άλλες επιλογές σύνδεσης" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Εγγραφή" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Εγγραφή" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Έχετε ήδη λογαριασμό; Τότε παρακαλούμε %(link)sσυνδεθείτε%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Εγγραφείτε χρησιμοποιώντας ένα κλειδί πρόσβασης" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Εγγραφή με κλειδί πρόσβασης" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Άλλες επιλογές" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Φραγή Εγγραφών" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Ζητούμε συγνώμη, αλλά η δυνατότητα εγγραφής είναι υπό φραγή." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Σημείωση" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Έχετε ήδη συνδεθεί ως %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Προσοχη:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Προς το παρόν δεν έχετε δημιουργήσει καμία διεύθυνση ηλεκτρονικού " "ταχυδρομείου. Θα πρέπει πραγματικά να προσθέσετε μια διεύθυνση ηλεκτρονικού " "ταχυδρομείου, ώστε να μπορείτε να λαμβάνετε ειδοποιήσεις, να επαναφέρετε τον " "κωδικό πρόσβασής σας κ.λπ." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Επιβεβαιώστε την διεύθυνση e-mail σας" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Σας στείλαμε ένα email για επαλήθευση. Ακολουθήστε το σύνδεσμο που παρέχεται " "για να ολοκληρώσετε τη διαδικασία εγγραφής. Εάν δεν βλέπετε το email " "επαλήθευσης στα κύρια εισερχόμενά σας, ελέγξτε το φάκελο ανεπιθύμητης " "αλληλογραφίας. Παρακαλούμε επικοινωνήστε μαζί μας εάν δεν λάβετε το email " "επαλήθευσης μέσα σε λίγα λεπτά." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Αυτό το τμήμα του ιστότοπου απαιτεί να επαληθεύσουμε ότι\n" "είστε αυτός που ισχυρίζεστε ότι είστε. Για το σκοπό αυτό, απαιτούμε να\n" "επαληθεύσετε την κυριότητα της διεύθυνσης ηλεκτρονικού ταχυδρομείου σας. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Σας στείλαμε ένα email για\n" "επαλήθευση. Παρακαλούμε κάντε κλικ στο σύνδεσμο μέσα στο email. Εάν δεν " "βλέπετε το email επαλήθευσης στα κύρια εισερχόμενά σας, ελέγξτε το φάκελο " "ανεπιθύμητης αλληλογραφίας. Διαφορετικά\n" "επικοινωνήστε μαζί μας εάν δεν το λάβετε μέσα σε λίγα λεπτά." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Note: μπορείτε ακόμη αλλάξτε τη " "διεύθυνση email σας." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Μηνύματα:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Μενού:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Συνδέσεις Λογαριασμού" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Έλεγχος ταυτότητας δύο παραγόντων" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Συνεδρίες" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Εξουσιοδότηση" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "" "Η εφαρμογή %(client_name)s θέλει πρόσβαση στο λογαριασμό σας %(site_name)s." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Εισάγετε τον κωδικό συσκευής" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Εισάγετε τον κωδικό που εμφανίζεται στη συσκευή σας." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Συνέχεια" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Επιβεβαίωση συσκευής" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Παρακαλούμε επιβεβαιώστε τον κωδικό που εμφανίζεται στο %(client_name)s σας " "για να εξουσιοδοτήσετε αυτή τη συσκευή." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Απόρριψη" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Η συσκευή εξουσιοδοτήθηκε" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Εξουσιοδοτήσατε επιτυχώς τη συσκευή %(client_name)s σας." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Η συσκευή απορρίφθηκε" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Η εξουσιοδότηση για τη συσκευή %(client_name)s σας απορρίφθηκε." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Σφάλμα" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Παραμονή σε σύνδεση" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Ο λογαριασμός σας προστατεύεται με έλεγχο ταυτότητας δύο παραγόντων. " "Παρακαλούμε εισάγετε έναν κωδικό ελέγχου ταυτότητας:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Έχει δημιουργηθεί ένα νέο σύνολο κωδικών ανάκτησης για τον έλεγχο ταυτότητας " "δύο παραγόντων." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Δημιουργία νέων κωδικών ανάκτησης" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Η εφαρμογή Authenticator ενεργοποιήθηκε." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Ενεργοποίηση της εφαρμογής Authenticator" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Η εφαρμογή Authenticator απενεργοποιήθηκε." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Απενεργοποιημένη εφαρμογή Αυθεντικοποιητή" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Έχει προστεθεί ένα νέο κλειδί ασφαλείας." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Προστέθηκε κλειδί ασφαλείας" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Ένα κλειδί ασφαλείας έχει αφαιρεθεί." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Το κλειδί ασφαλείας αφαιρέθηκε" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Εφαρμογή Αυθεντικοποιητή" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "" "Ο έλεγχος ταυτότητας με χρήση μιας εφαρμογής ελέγχου ταυτότητας είναι " "ενεργός." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Μια εφαρμογή ελέγχου ταυτότητας δεν είναι ενεργή." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Απενεργοποίηση" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Ενεργοποίηση" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Κλειδιά ασφαλείας" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Έχετε προσθέσει το κλειδί ασφαλείας %(count)s." msgstr[1] "Έχετε προσθέσει %(count)s κλειδιά ασφαλείας." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Δεν έχουν προστεθεί κλειδιά ασφαλείας." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Διαχείριση" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Προσθήκη" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Κωδικοί Aνάκτησης" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Υπάρχει %(unused_count)s από τους %(total_count)s διαθέσιμους κωδικούς " "ανάκτησης." msgstr[1] "" "Υπάρχουν %(unused_count)s από τους %(total_count)s διαθέσιμους κωδικούς " "ανάκτησης." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Δεν έχουν οριστεί κωδικοί ανάκτησης." #: templates/mfa/index.html:96 msgid "View" msgstr "Προβολή" #: templates/mfa/index.html:102 msgid "Download" msgstr "Λήψη" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Παραγάγετε" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Δημιουργήθηκε ένα νέο σύνολο κωδικών ανάκτησης." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Προστέθηκε κλειδί ασφαλείας." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Το κλειδί ασφαλείας αφαιρέθηκε." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Εισάγετε έναν κωδικό ελέγχου ταυτότητας:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "" "Πρόκειται να δημιουργήσετε ένα νέο σύνολο κωδικών ανάκτησης για το " "λογαριασμό σας." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Αυτή η ενέργεια θα ακυρώσει τους υπάρχοντες κωδικούς σας." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Είσαι σίγουρος;" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Αχρησιμοποίητοι κωδικοί" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Λήψη κωδικών" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Δημιουργία νέων κωδικών" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Ενεργοποιήστε την εφαρμογή Authenticator" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Για να προστατέψετε το λογαριασμό σας με έλεγχο ταυτότητας δύο παραγόντων, " "σαρώστε τον παρακάτω κωδικό QR με την εφαρμογή ελέγχου ταυτότητας. Στη " "συνέχεια, εισαγάγετε τον κωδικό επαλήθευσης που δημιουργείται από την " "παρακάτω εφαρμογή." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Μυστικό αυθεντικοποιητή" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Μπορείτε να αποθηκεύσετε αυτό το μυστικό και να το χρησιμοποιήσετε για να " "επανεγκαταστήσετε την εφαρμογή ελέγχου ταυτότητας σε μεταγενέστερο χρόνο." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Απενεργοποίηση της εφαρμογής Authenticator" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Πρόκειται να απενεργοποιήσετε τον έλεγχο ταυτότητας με βάση την εφαρμογή " "authenticator. Είστε σίγουροι;" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Εμπιστεύεστε αυτό το πρόγραμμα περιήγησης;" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Εάν επιλέξετε να εμπιστευτείτε αυτό το πρόγραμμα περιήγησης, δεν θα σας " "ζητηθεί κωδικός επαλήθευσης την επόμενη φορά που θα συνδεθείτε." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Εμπιστοσύνη για %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Μην εμπιστεύεστε" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Προσθήκη κλειδιού ασφαλείας" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Αφαίρεση κλειδιού ασφαλείας" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Είστε σίγουροι ότι θέλετε να αφαιρέσετε αυτό το κλειδί ασφαλείας;" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Χρήση" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Κλειδί πρόσβασης" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Κλειδί ασφαλείας" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Αυτό το κλειδί δεν υποδεικνύει αν είναι κλειδί πρόσβασης." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Απροσδιόριστο" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Προστέθηκε στις %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Τελευταία χρήση %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Επεξεργασία" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Επεξεργασία κλειδιού ασφαλείας" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Αποθήκευση" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Δημιουργία κλειδιού πρόσβασης" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Πρόκειται να δημιουργήσετε ένα κλειδί πρόσβασης για το λογαριασμό σας. Καθώς " "μπορείτε να προσθέσετε επιπλέον κλειδιά αργότερα, μπορείτε να " "χρησιμοποιήσετε ένα περιγραφικό όνομα για να ξεχωρίζετε τα κλειδιά." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Δημιουργία" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Αυτή η λειτουργία απαιτεί JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Αποτυχία σύνδεσης τρίτου μέρους" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Παρουσιάστηκε σφάλμα κατά την προσπάθεια σύνδεσης μέσω του λογαριασμού σας " "τρίτου μέρους." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Μπορείτε να συνδεθείτε στο λογαριασμό σας χρησιμοποιώντας οποιονδήποτε από " "τους ακόλουθους λογαριασμούς τρίτων:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "" "Προς το παρόν δεν έχετε συνδέσει λογαριασμούς τρίτων με αυτόν το λογαριασμό." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Προσθήκη λογαριασμού τρίτου μέρους" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "" "Ένας λογαριασμός τρίτου μέρους από τον %(provider)s έχει συνδεθεί με το " "λογαριασμό σας." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Συνδεδεμένος λογαριασμός τρίτου μέρους" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" "Ένας λογαριασμός τρίτου μέρους από τον %(provider)s έχει αποσυνδεθεί από το " "λογαριασμό σας." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Αποσυνδεδεμένος λογαριασμός τρίτου μέρους" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Συνδέστε %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "" "Πρόκειται να συνδέσετε έναν νέο λογαριασμό τρίτου μέρους από τον " "%(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Συνδεθείτε με %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" "Πρόκειται να συνδεθείτε χρησιμοποιώντας έναν λογαριασμό τρίτου μέρους από " "τον %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Σύνδεση ακυρώθηκε" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Αποφασίσατε να ακυρώσετε την σύνδεση σας στην ιστοσελίδα με έναν από τους " "υπάρχοντες λογαριασμούς σας. Αν έγινε κατά λάθος, παρακαλώ συνδεθείτε." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Ο λογαριασμός τρίτου μέρους έχει συνδεθεί." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Ο λογαριασμός τρίτου μέρους έχει αποσυνδεθεί." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Πρόκειται να χρησιμοποιήσετε τον %(provider_name)s λογαριασμό σας για να " "συνδεθείτε στην σελίδα\n" "%(site_name)s. Ως τελικό βήμα, παρακαλούμε συμπληρώστε την παρακάτω φόρμα:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Ή χρησιμοποιήστε ένα τρίτο μέρος" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Αποχώρησε από όλες τις άλλες συνεδρίες." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Ξεκίνησε στο" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "Διεύθυνση IP" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Πρόγραμμα περιήγησης" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Τελευταία φορά στο" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Τρέχον" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Αποχώρηση από άλλες συνεδρίες" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Συνεδρίες χρηστών" #: usersessions/models.py:94 msgid "session key" msgstr "κλειδί συνεδρίας" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Συνδέσεις Λογαριασμού" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Το συνθηματικό πρέπει να περιέχει τουλάχιστον {0} χαρακτήρες." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Χαιρετίσματα από το %(site_name)s!\n" #~ "\n" #~ "Λαμβάνετε αυτό το e-mail επειδή εσείς ή κάποιος άλλος έχει κάνει αίτηση " #~ "συνθηματικού για τον λογαριασμό σας.\n" #~ "Αν δεν ζητήσατε επαναφορά συνθηματικού, μπορεί να αγνοηθεί με ασφάλεια. " #~ "Πατήστε στον σύνδεσμο που ακολουθεί για να επαναφέρετε το συνθηματικό σας." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "" #~ "Οι διευθύνσεις e-mail που ακολουθούν είναι συσχετισμένες με τον " #~ "λογαριασμό σας:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Επιβεβαίωση διεύθυνση e-mail" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Παρακαλούμε συνδεθείτε με έναν\n" #~ "από τους ήδη υπάρχοντες εξωτερικούς λογαριασμούς σας. Ή, κάντε εγγραφή\n" #~ "για έναν λογαριασμό %(site_name)s και συνδεθείτε παρακάτω:" #~ msgid "or" #~ msgstr "ή" #~ msgid "change password" #~ msgstr "αλλαγή συνθηματικού" #~ msgid "OpenID Sign In" #~ msgstr "Σύνδεση OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "Αυτό το e-mail χρησιμοποιείται ήδη από άλλο λογαριασμό." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Σας έχουμε στείλει ένα e-mail. Παρακαλούμε επικοινωνήστε μαζί μας αν δεν " #~ "το έχετε παραλάβει μέσα σε λίγα λεπτά." ================================================ FILE: allauth/locale/en/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-02-13 09:05-0600\n" "PO-Revision-Date: 2023-07-24 22:28+0200\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "" #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "" #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "" #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "" #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "" #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "" #: account/adapter.py:75 msgid "Please type your current password." msgstr "" #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "" #: account/adapter.py:77 msgid "Incorrect password." msgstr "" #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "" #: account/adapter.py:79 msgid "Invalid login." msgstr "" #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "" #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "" #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "" #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "" #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "" #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "" #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "" #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "" #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "" #: account/adapter.py:98 msgid "Please select only one." msgstr "" #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "" #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "" #: account/adapter.py:826 msgid "Use your password" msgstr "" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "" #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "" #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "" #: account/apps.py:11 msgid "Accounts" msgstr "" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "" #: account/fields.py:19 msgid "Email address" msgstr "" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "" #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "" #: account/forms.py:67 msgid "Remember Me" msgstr "" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "" #: account/forms.py:115 msgid "Username, email or phone" msgstr "" #: account/forms.py:117 msgid "Username or email" msgstr "" #: account/forms.py:119 msgid "Username or phone" msgstr "" #: account/forms.py:121 msgid "Email or phone" msgstr "" #: account/forms.py:144 msgid "Forgot your password?" msgstr "" #: account/forms.py:287 msgid "Email (again)" msgstr "" #: account/forms.py:292 msgid "Email address confirmation" msgstr "" #: account/forms.py:302 msgid "Email (optional)" msgstr "" #: account/forms.py:314 msgid "Username (optional)" msgstr "" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "" #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "" #: account/forms.py:591 msgid "Current Password" msgstr "" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "" #: account/models.py:23 msgid "user" msgstr "" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "" #: account/models.py:31 msgid "verified" msgstr "" #: account/models.py:32 msgid "primary" msgstr "" #: account/models.py:38 msgid "email addresses" msgstr "" #: account/models.py:142 msgid "created" msgstr "" #: account/models.py:143 msgid "sent" msgstr "" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "" #: account/models.py:149 msgid "email confirmation" msgstr "" #: account/models.py:150 msgid "email confirmations" msgstr "" #: headless/apps.py:7 msgid "Headless" msgstr "" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "" #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "" #: idp/oidc/models.py:38 msgid "Device code" msgstr "" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "" #: idp/oidc/models.py:44 msgid "Public" msgstr "" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" #: idp/oidc/models.py:115 msgid "client" msgstr "" #: idp/oidc/models.py:116 msgid "clients" msgstr "" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "" #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" #: mfa/adapter.py:141 msgid "Master key" msgstr "" #: mfa/adapter.py:143 msgid "Backup key" msgstr "" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "" #: mfa/apps.py:9 msgid "MFA" msgstr "" #: mfa/models.py:24 msgid "Recovery codes" msgstr "" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "" #: mfa/models.py:26 msgid "WebAuthn" msgstr "" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "" #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "" #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "" #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "" #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "" #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "" #: socialaccount/models.py:53 msgid "provider ID" msgstr "" #: socialaccount/models.py:57 msgid "name" msgstr "" #: socialaccount/models.py:59 msgid "client id" msgstr "" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "" #: socialaccount/models.py:64 msgid "secret key" msgstr "" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "" #: socialaccount/models.py:82 msgid "social application" msgstr "" #: socialaccount/models.py:83 msgid "social applications" msgstr "" #: socialaccount/models.py:118 msgid "uid" msgstr "" #: socialaccount/models.py:120 msgid "last login" msgstr "" #: socialaccount/models.py:121 msgid "date joined" msgstr "" #: socialaccount/models.py:122 msgid "extra data" msgstr "" #: socialaccount/models.py:126 msgid "social account" msgstr "" #: socialaccount/models.py:127 msgid "social accounts" msgstr "" #: socialaccount/models.py:161 msgid "token" msgstr "" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "" #: socialaccount/models.py:166 msgid "token secret" msgstr "" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "" #: socialaccount/models.py:170 msgid "expires at" msgstr "" #: socialaccount/models.py:175 msgid "social application token" msgstr "" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "" #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "" #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "" #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "" #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "" #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "" #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "" #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "" #: templates/account/email.html:25 msgid "Verified" msgstr "" #: templates/account/email.html:29 msgid "Unverified" msgstr "" #: templates/account/email.html:34 msgid "Primary" msgstr "" #: templates/account/email.html:44 msgid "Make Primary" msgstr "" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "" #: templates/account/email.html:70 msgid "Add Email" msgstr "" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "" #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "" #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "" #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "" #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "" #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "" #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "" #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "" #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "" #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "" #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "" #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "" #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "" #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "" #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "" #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "" #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "" #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "" #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "" #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "" #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "" #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "" #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "" #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "" #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "" #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "" #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "" #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "" #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "" #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "" #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "" #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "" #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "" #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "" #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "" #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "" #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "" msgstr[1] "" #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "" #: templates/mfa/index.html:62 msgid "Manage" msgstr "" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" msgstr[1] "" #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "" #: templates/mfa/index.html:96 msgid "View" msgstr "" #: templates/mfa/index.html:102 msgid "Download" msgstr "" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "" #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "" #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "" #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "" #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "" #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "" #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "" #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "" #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "" #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "" #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "" #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "" #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "" #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "" #: usersessions/models.py:94 msgid "session key" msgstr "" ================================================ FILE: allauth/locale/es/LC_MESSAGES/django.po ================================================ # Traducción al castellano de django-allauth # Copyright (C) 2024 # Quique , 2024. # msgid "" msgstr "" "Project-Id-Version: 0.61.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-11-11 12:51+0000\n" "Last-Translator: Federico Bond \n" "Language-Team: Spanish \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Weblate 5.15-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Esta cuenta se encuentra ahora mismo desactivada." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "No puede eliminar su correo electrónico principal." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Esta dirección de correo electrónico ya está asociada a esta cuenta." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "" "La dirección de correo electrónico y/o la contraseña que ha indicado no son " "correctas." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "" "El número de teléfono y/o la contraseña que ha indicado no son correctos." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Ya hay registrado un usuario con esta dirección de correo electrónico." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Por favor, escriba su contraseña actual." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Código incorrecto." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Contraseña incorrecta." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Clave no válida o caducada." #: account/adapter.py:79 msgid "Invalid login." msgstr "Login inválido." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "El token para restablecer la contraseña no es válido." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "No puede añadir más de %d direcciones de correo electrónico." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Ya hay registrado un usuario con este número de teléfono." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Demasiados intentos fallidos. Inténtelo más tarde." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "" "Esta dirección de correo electrónico no está asignada a ninguna cuenta de " "usuario." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "El número de teléfono no está asignado a ninguna cuenta de usuario." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Su dirección principal de correo electrónico debe ser verificada." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "No se puede usar este nombre de usuario. Por favor, utilice otro." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "El usuario y/o la contraseña que ha indicado no son correctos." #: account/adapter.py:98 msgid "Please select only one." msgstr "Por favor selecciona solo uno." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "El nuevo valor debe ser diferente del actual." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Ten paciencia, estás enviando muchas solicitudes." #: account/adapter.py:826 msgid "Use your password" msgstr "Utilice su contraseña" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Utiliza la aplicación o el código de autenticación" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Usar una clave de seguridad" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} se ha marcado como verificado." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "No se pudo marcar {email} como verificado." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "" "Marcar las direcciones de correo electrónico seleccionadas como verificadas" #: account/apps.py:11 msgid "Accounts" msgstr "Cuentas" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "Correo electrónico" #: account/fields.py:19 msgid "Email address" msgstr "Dirección de correo electrónico" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Introduce un número incluyendo el código del país (ej. +34 para ES)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Teléfono" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Debe escribir la misma contraseña cada vez." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Contraseña" #: account/forms.py:67 msgid "Remember Me" msgstr "Recordarme" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Usuario" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Iniciar sesión" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Usuario, correo o teléfono" #: account/forms.py:117 msgid "Username or email" msgstr "Usuario o correo electrónico" #: account/forms.py:119 msgid "Username or phone" msgstr "Usuario o teléfono" #: account/forms.py:121 msgid "Email or phone" msgstr "Correo electrónico o teléfono" #: account/forms.py:144 msgid "Forgot your password?" msgstr "¿Ha olvidado tu contraseña?" #: account/forms.py:287 msgid "Email (again)" msgstr "Correo electrónico (otra vez)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Confirmación de la dirección de correo electrónico" #: account/forms.py:302 msgid "Email (optional)" msgstr "Correo electrónico (opcional)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Usuario (opcional)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Debe escribir el mismo correo electrónico cada vez." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Contraseña (de nuevo)" #: account/forms.py:591 msgid "Current Password" msgstr "Contraseña actual" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Nueva contraseña" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Nueva contraseña (de nuevo)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Código" #: account/models.py:23 msgid "user" msgstr "usuario" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "dirección de correo electrónico" #: account/models.py:31 msgid "verified" msgstr "verificado" #: account/models.py:32 msgid "primary" msgstr "principal" #: account/models.py:38 msgid "email addresses" msgstr "direcciones de correo electrónico" #: account/models.py:142 msgid "created" msgstr "creado" #: account/models.py:143 msgid "sent" msgstr "enviado" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "clave" #: account/models.py:149 msgid "email confirmation" msgstr "confirmación de correo electrónico" #: account/models.py:150 msgid "email confirmations" msgstr "confirmaciones de correo electrónico" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Ver tu ID de usuario" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Ver su dirección de correo electrónico" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Ver tu información básica de perfil" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Otorgar permisos" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "No se permiten comodines a menos que se habilite 'Permitir comodines en URI'." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "El URI '{}' contiene más de un comodín (*). Solo se permite un comodín por " "URI." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Los comodines solo se permiten en la parte del nombre de host del URI." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Código de autorización" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Código del dispositivo" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Credenciales de cliente" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Refrescar token" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Confidencial" #: idp/oidc/models.py:44 msgid "Public" msgstr "Público" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Los alcances que el cliente puede solicitar. Proporcione un valor por línea, " "por ejemplo: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Si el cliente no especifica ningún alcance, se utilizan estos alcances " "predeterminados. Proporcione un valor por línea, por ejemplo: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Una lista de tipos de concesión permitidos. Proporcione un valor por línea, " "por ejemplo: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Una lista de orígenes permitidos para solicitudes de origen cruzado, uno por " "línea." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Permitir comodines (*) en URIs de redirección y orígenes CORS. Cuando está " "habilitado, los URIs pueden contener un solo asterisco para coincidir con " "subdominios." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Una lista de tipos de respuesta permitidos. Proporcione un valor por línea, " "por ejemplo: code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "cliente" #: idp/oidc/models.py:116 msgid "clients" msgstr "clientes" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "No puede añadir una dirección de correo electrónico a una cuenta protegida " "por la autenticación de dos factores." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "No se puede desactivar la autenticación de dos factores." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "No puedes generar códigos de recuperación sin tener activada la " "autenticación de dos factores." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "No puede activar la autenticación de dos factores hasta que haya verificado " "su dirección de correo electrónico." #: mfa/adapter.py:141 msgid "Master key" msgstr "Llave maestra" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Clave de respaldo" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Clave n° {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Códigos de recuperación" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "Autenticador TOTP" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Código del autenticador" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Passwordless" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Habilitar la operación sin contraseña le permite iniciar sesión usando solo " "esta clave, pero impone requisitos adicionales como biometría o protección " "con PIN." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Ya existe una cuenta con esta dirección de correo electrónico. Por favor, " "primero inicie sesión con esa cuenta, y después vincule su cuenta %s." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Token no válido." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Su cuenta no tiene ninguna contraseña." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Su cuenta no tiene ninguna dirección de correo electrónico verificada." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "No puede desconectar la última cuenta de terceros restante." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Esta cuenta externa ya está asociada a otra cuenta." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Cuentas de redes sociales" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "proveedor" #: socialaccount/models.py:53 msgid "provider ID" msgstr "ID del proveedor" #: socialaccount/models.py:57 msgid "name" msgstr "nombre" #: socialaccount/models.py:59 msgid "client id" msgstr "identificador del cliente" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "Identificador de App, o clave de consumidor" #: socialaccount/models.py:64 msgid "secret key" msgstr "clave secreta" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "" "Clave secreta de la API, clave secreta del cliente o clave secreta del " "consumidor" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Clave" #: socialaccount/models.py:82 msgid "social application" msgstr "aplicación de redes sociales" #: socialaccount/models.py:83 msgid "social applications" msgstr "aplicaciones de redes sociales" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "último inicio de sesión" #: socialaccount/models.py:121 msgid "date joined" msgstr "fecha de incorporación" #: socialaccount/models.py:122 msgid "extra data" msgstr "datos extra" #: socialaccount/models.py:126 msgid "social account" msgstr "cuenta de redes sociales" #: socialaccount/models.py:127 msgid "social accounts" msgstr "cuentas de redes sociales" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) o token de acceso (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "clave secreta del token" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) o token de refresco (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "expira el" #: socialaccount/models.py:175 msgid "social application token" msgstr "token de aplicación de redes sociales" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "tokens de aplicación de redes sociales" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Datos de perfil no válidos" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Iniciar sesión" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Cancelar" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Respuesta no válida al obtener token de solicitud de \"%s\". La respuesta " "fue: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Respuesta no válida al obtener el token de acceso de \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "No hay token de solicitud guardado para \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "No hay token de acceso guardado para \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Sin acceso a recursos privados de \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Respuesta no válida al obtener token de solicitud de \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Cuenta desactivada" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Esta cuenta está desactivada." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Hemos enviado un código a %(recipient)s. El código caduca en breve, así que " "introdúcelo pronto." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Confirmar" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Solicitar nuevo código" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Confirmar acceso" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Por favor, vuelva a autenticarse para proteger su cuenta." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Opciones alternativas" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Verificación del correo electrónico" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Introduce el código de verificación del correo electrónico" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Usar una dirección de correo electrónico diferente" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Iniciar sesión" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Ingrese el código de inicio de sesión" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Restablecer Contraseña" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Introduce el código · Restablecer Contraseña" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Reenviar Verificación telefónica" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Introduce el código de verificación del teléfono" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Usar un número de teléfono diferente" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Direcciones de correo electrónico" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "" "Las siguientes direcciones de correo electrónico están asociadas a su cuenta:" #: templates/account/email.html:25 msgid "Verified" msgstr "Verificado" #: templates/account/email.html:29 msgid "Unverified" msgstr "Sin verificar" #: templates/account/email.html:34 msgid "Primary" msgstr "Principal" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Definir como principal" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Reenviar Verificación" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Eliminar" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Añadir correo electrónico" #: templates/account/email.html:70 msgid "Add Email" msgstr "Añadir correo electrónico" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "" "¿Seguro que desea eliminar la dirección de correo electrónico seleccionada?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Usted está recibiendo este correo electrónico porque usted u otra persona " "intentó registrarse para una\n" "cuenta utilizando la siguiente dirección de correo electrónico:\n" "\n" "%(email)s\n" "\n" "Sin embargo, ya existe una cuenta con esa dirección de correo electrónico. " "En caso de que lo haya\n" "olvidado, utilice el procedimiento de recuperació de contraseña olvidada " "para recuperar\n" "su cuenta:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "La cuenta ya existe" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "¡Hola de parte de %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "¡Gracias por usar %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Está recibiendo este correo porque se ha realizado el siguiente cambio en su " "cuenta:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Si no reconoce este cambio, tome inmediatamente las precauciones de " "seguridad adecuadas. El cambio en su cuenta se origina en:\n" "\n" "- Dirección IP: %(ip)s\n" "- Navegador: %(user_agent)s\n" "- Fecha: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Tu email ha sido cambiado de %(from_email)s a %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Correo electrónico modificado" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Su correo electrónico ha sido verificado." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Correo electrónico confirmado" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Estás recibiendo este correo electrónico porque el usuario %(user_display)s " "ha facilitado tu dirección de correo electrónico para registrar una cuenta " "en %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Tu código de verificación de correo electrónico aparece a continuación. Por " "favor, introdúcelo en la ventana abierta de tu navegador." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Para confirmar que esto es correcto, vete a %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Por favor, confirme su dirección de correo electrónico" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "" "La dirección de correo electrónico %(deleted_email)s ha sido eliminada de tu " "cuenta." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Correo electrónico eliminado" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Su código de inicio de sesión se enumera a continuación. Introdúzcalo en la " "ventana abierta de su navegador." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Este correo puede ser ignorado con seguridad si usted no inició esta acción." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Código de inicio de sesión" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Su contraseña ha sido modificada." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Contraseña modificada" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Su código de restablecimiento de contraseña se enumera a continuación. " "Introdúzcalo en la ventana abierta de su navegador." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Código de restablecimiento de contraseña" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Está recibiendo este correo electrónico porque usted u otra persona ha " "solicitado un restablecimiento de contraseña para su cuenta de usuario.\n" "Puede ignorar este correo si no ha solicitado este cambio. Haga clic en el " "siguiente enlace para restablecer su contraseña." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "En caso de haberlo olvidado, su usuario es %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Correo electrónico para restablecer contraseña" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Su contraseña ha sido restablecida." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Su contraseña ha sido establecida." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Establecer contraseña" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Está recibiendo este correo electrónico porque usted, u otra persona, " "intentó acceder a una cuenta con email %(email)s. Sin embargo, no tenemos " "constancia de dicha cuenta en nuestra base de datos." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Si ha sido usted, puede registrarse para obtener una cuenta utilizando el " "siguiente enlace." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Cuenta desconocida" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Correo electrónico" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Correo electrónico actual" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Cambiando a" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Su dirección de correo electrónico sigue pendiente de verificación." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Cancelar el cambio" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Cambiar a" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Cambiar correo electrónico" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Confirmar dirección de correo electrónico" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Por favor, confirme que %(email)s es una " "dirección de correo electrónico para el usuario %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "No se puede confirmar %(email)s porque ya está confirmado por otra cuenta." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Este enlace de confirmación por correo electrónico ha caducado o no es " "válido. Por favor, emita una nueva solicitud de " "confirmación por correo electrónico." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Si aún no ha creado una cuenta, por favor %(link)sregístrese%(end_link)s " "primero." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Iniciar sesión con un passkey" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Envíame un código de inicio de sesión" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Cerrar sesión" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "¿Seguro que desea cerrar sesión?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "No puede eliminar su correo electrónico principal (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Correo electrónico de confirmación enviado a %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Ha confirmado %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Correo electrónico %(email)s eliminado." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Ha iniciado sesión exitosamente como %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Ha cerrado sesión." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "" "Se ha enviado un código de inicio de sesión por correo a %(recipient)s ." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Contraseña cambiada con éxito." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Contraseña establecida con éxito." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Se ha enviado un código de verificación a %(phone)s ." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Has verificado el número de teléfono %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Dirección principal de correo electrónico establecida." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Cambiar la contraseña" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "¿Olvidó su contraseña?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "¿Ha olvidado su contraseña? Introduzca su dirección de correo electrónico, y " "le enviaremos un correo que le permitirá restablecerla." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Restablecer mi contraseña" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Si tiene alguna dificultad para restablecer su contraseña, por favor " "contáctenos." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Le hemos enviado un correo electrónico. Si no lo ha recibido, compruebe su " "carpeta de correo no deseado. De lo contrario, póngase en contacto con " "nosotros si no lo recibe en unos minutos." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Hay un problema con el token" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "El enlace para restablecer la contraseña es inválido, probablemente porque " "ya ha sido utilizado. Por favor solicite restablecer la contraseña de nuevo." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Su contraseña ha cambiado." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Establecer contraseña" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Cambiar a teléfono" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Teléfono actual" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Su número de teléfono sigue pendiente de verificación." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Cambiar a teléfono" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Introduzca su contraseña:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Recibirá un código especial para iniciar sesión sin contraseña." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Solicite el código" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Otras opciones de inicio de sesión" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Registro" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Registrarse" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "¿Ya tiene una cuenta? Por favor %(link)sinicie sesión%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Regístrate usando una passkey" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Regítrate con passkey" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Otras opciones" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Registro cerrado" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Lo sentimos, en este momento el registro está cerrado." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Nota" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Ya ha iniciado sesión como %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Advertencia:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Actualmente no tienes ninguna dirección de correo electrónico configurada. " "Debería añadir una dirección de correo electrónico para recibir " "notificaciones, restablecer su contraseña, etc." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Verifique su dirección de correo electrónico" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Le hemos enviado un correo electrónico para su verificación. Siga el enlace " "proporcionado para finalizar el proceso de registro. Si no ves el correo " "electrónico de verificación en tu bandeja de entrada principal, comprueba tu " "carpeta de correo no deseado. Por favor, póngase en contacto con nosotros si " "no recibe el correo electrónico de verificación en unos minutos." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Esta parte de la página requiere que verifiquemos que\n" "usted es quien dice ser. Para ello, le pedimos que\n" "verifique la titularidad de su dirección de correo electrónico. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Le hemos enviado un correo electrónico para\n" "verificación. Por favor, haga clic en el enlace que aparece en ese correo " "electrónico. Si no ve el correo electrónico de verificación en su bandeja de " "entrada principal, compruebe su carpeta de correo no deseado. De lo " "contrario,\n" "póngase en contacto con nosotros si no lo recibe en unos minutos." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Nota: aún puedes cambiar tu " "dirección de correo electrónico." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Mensajes:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menú:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Conexiones de Cuenta" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Autenticació de doble factor" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sesiones" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Autorizar" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s quiere acceder a tu cuenta de %(site_name)s." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Ingrese el código del dispositivo" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Ingrese el código que se muestra en su dispositivo." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Continuar" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Confirmar dispositivo" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Por favor confirme el código que se muestra en su %(client_name)s para " "autorizar este dispositivo." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Rechazar" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Dispositivo autorizado" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Has autorizado satisfactoriamente tu dispositivo %(client_name)s." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Dispositivo rechazado" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "La autorización para tu dispositivo %(client_name)s fue rechazada." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Error" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Mantener la sesión iniciada" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Su cuenta está protegida por doble factor de autenticación. Por favor, entre " "el código del autenticador:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Se ha generado un nuevo conjunto de códigos de recuperación de Autenticación " "de Dos Factores." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Generados nevos códigos de recuperación" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "La aplicación de autenticación ha sido activada." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Aplicación de autenticación activada" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Aplicación de autenticación desactivada." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Aplicación de autenticación desactivada" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Se ha agregado una nueva clave de seguridad." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Clave de seguridad añadida" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Se ha eliminado una clave de seguridad." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Clave de seguridad eliminada" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Aplicación de autenticación" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "La autenticación con una aplicación de autenticación está activa." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "No hay ninguna aplicación de autenticación activa." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Desactivar" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Activar" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Claves de seguridad" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Has añadido %(count)s clave." msgstr[1] "Has añadido %(count)s claves." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "No se han agregado claves de seguridad." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Administrar" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Añadir" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Códigos de recuperación" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Hay %(unused_count)s de %(total_count)s código de recuperación disponibles." msgstr[1] "" "Hay %(unused_count)s de %(total_count)s códigos de recuperación disponibles." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "No se han configurado códigos de recuperación." #: templates/mfa/index.html:96 msgid "View" msgstr "Mostrar" #: templates/mfa/index.html:102 msgid "Download" msgstr "Descargar" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Generar" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Se ha generado un nuevo conjunto de códigos de recuperación." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Clave de seguridad añadida." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Clave de seguridad eliminada." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Ingrese un código de autenticación:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "" "Está a punto de generar un nuevo conjunto de código de recuperación para su " "cuenta." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Esta operación invalidará sus códigos existentes." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "¿Está seguro?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Códigos no utilizados" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Descarga los códigos" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Crear nuevos códigos" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Activar la aplicación de autenticación" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Escanee el código QR a continuación con su aplicación de autenticación para " "proteger su cuenta con autenticación de dos factores. Luego, ingrese el " "código de verificación generado por la aplicación a continuación." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Secreto de autenticador" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Puede guardar este secreto y usarlo más tarde para reinstalar su aplicación " "de autenticación." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Deshabilitar la aplicación de autenticación" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Está a punto de desactivar la autenticación a través de la aplicación de " "autenticación. ¿Está seguro?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Confía en este navegador?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Si decides confiar en este navegador, no se te pedirá un código de " "verificación la próxima vez que inicies sesión." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Confía durante %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "No confíes" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Añadir una clave de seguridad" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Eliminar clave de seguridad" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "¿Estás seguro de que deseas eliminar esta clave de seguridad?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Uso" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Passkey" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Clave de seguridad" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Esta clave no indica si es una clave de acceso." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Sin especificar" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Añadida el %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Usada por última vez %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Editar" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Editar la clave de seguridad" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Guardar" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Crear una passkey" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Estás a punto de crear una passkey para tu cuenta. Como puedes añadir claves " "adicionales más adelante, puedes utilizar un nombre descriptivo para " "diferenciar las claves." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Crear" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Esta funcionalidad requiere JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Falló la autenticación de terceros" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Se produjo un error al intentar iniciar sesión a través de su cuenta de " "terceros." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Puede iniciar sesión en su cuenta utilizando una de las siguientes cuentas " "de terceros:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Actualmente no tiene cuentas de terceros conectadas a esta cuenta." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Agregar una cuenta de terceros" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Una cuenta de terceros de %(provider)s se ha conectado a su cuenta." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Cuenta de terceros vinculada" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" "La cuenta de terceros desde %(provider)s ha sido desvinculada de tu cuenta." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Cuenta de terceros desconectada" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Conectar %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Se dispone a conectarse a una nueva cuenta externa de %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Inicio de sesión mediante %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" "Se dispone a iniciar sesión usando una cuenta externa desde %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Inicio de sesión cancelado" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Ha decidido cancelar el inicio de sesión en nuestro sitio usando una de sus " "cuentas. Si ha sido un error, por favor inicie " "sesión." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "La cuenta externa ha sido conectada." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "La cuenta externa ha sido desconectada." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Se dispone a usar su cuenta de %(provider_name)s para acceder a " "%(site_name)s.\n" "Como paso final, por favor complete el siguiente formulario:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "O use una cuenta de terceros" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Se han cerrado todas las demás sesiones." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Iniciada el" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "Dirección IP" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Navegador" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Visto por última vez" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Actual" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Cerrar las demás sesiones" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Sesiones de usuario" #: usersessions/models.py:94 msgid "session key" msgstr "clave de sesión" #~ msgid "Account Connection" #~ msgstr "Conexiones de la cuenta" #~ msgid "Use security key or device" #~ msgstr "Usar clave o dispositivo de seguridad" #~ msgid "Add Security Key or Device" #~ msgstr "Agregar clave de seguridad o dispositivo" #~ msgid "Add key or device" #~ msgstr "Agregar clave o dispositivo" #~ msgid "Security Keys and Devices" #~ msgstr "Dispositivos y claves de seguridad" #~ msgid "You have not added any security keys/devices." #~ msgstr "No ha agregado ninguna clave/dispositivo de seguridad." #~ msgid "Edit Security Key or Device" #~ msgstr "Editar clave o dispositivo de seguridad" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "La contraseña necesita al menos {0} caracteres." #, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Está recibiendo este correo electrónico porque usted u otra persona ha " #~ "solicitado una\n" #~ "contraseña para su cuenta de usuario. Sin embargo, no tenemos ningún " #~ "registro de un usuario\n" #~ "con email %(email)s en nuestra base de datos.\n" #~ "\n" #~ "Este correo puede ser ignorado con seguridad si usted no solicitó un " #~ "restablecimiento de contraseña.\n" #~ "\n" #~ "Si has sido tú, puedes registrarte para obtener una cuenta utilizando el " #~ "siguiente enlace." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "" #~ "Las siguientes direcciones de correo electrónico están asociadas a su " #~ "cuenta:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Confirmar dirección de correo electrónico" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Por favor, inicie sesión con una\n" #~ "cuenta de otra red social. O %(link)sregístrese%(end_link)s \n" #~ "como usuario de %(site_name)s e inicie sesión a continuación:" #~ msgid "or" #~ msgstr "o" #~ msgid "change password" #~ msgstr "cambiar la contraseña" #~ msgid "OpenID Sign In" #~ msgstr "Iniciar sesión con OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "Este correo electrónico ya está asociado con otra cuenta." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Le hemos enviado un correo electrónico. Por favor contáctenos si no lo " #~ "recibe en unos minutos." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "" #~ "El correo electrónico/usuario y/o la contraseña que especificó no son " #~ "correctos." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "" #~ "Los nombres de usuarios pueden contener solamente letras, números, y @/./" #~ "+/-/_." #~ msgid "This username is already taken. Please choose another." #~ msgstr "Este usuario ya está en uso. Por favor elije otro." #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "Has confirmado que %(email)s es una " #~ "dirección de correo electrónico del usuario %(user_display)s." #~ msgid "Thanks for using our site!" #~ msgstr "¡Gracias por utilizar nuestro sitio!" #~ msgid "Confirmation email sent to %(email)s" #~ msgstr "Correo de confirmación enviado a %(email)s" #~ msgid "Delete Password" #~ msgstr "Eliminar Contraseña" #~ msgid "" #~ "You may delete your password since you are currently logged in using " #~ "OpenID." #~ msgstr "Puedes eliminar tu contraseña ya que ingresaste con OpenID." #~ msgid "delete my password" #~ msgstr "eliminar mi contraseña" #~ msgid "Password Deleted" #~ msgstr "Contraseña Eliminada" ================================================ FILE: allauth/locale/et/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2024-04-20 21:55+0200\n" "Last-Translator: Gregor Grigorjan \n" "Language-Team: LANGUAGE \n" "Language: Estonian\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "See konto on parajasti mitteaktiivne." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Primaarset e-posti aadressi ei saa eemaldada." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "See e-posti aadress on juba selle kontoga seostatud." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Sisestatud e-posti aadress või salasõna ei ole õige." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Sisestatud telefoninumber või salasõna ei ole õige." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Selle e-posti aadressiga on juba kasutaja registreeritud." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Palun sisesta oma praegune salasõna." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Vale kood." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Vale salasõna." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Vale või aegunud võti." #: account/adapter.py:79 msgid "Invalid login." msgstr "Vale sisselogimine." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Salasõna taasseadmise tooken oli vale." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Sa ei tohi lisada enam kui %d e-posti aadressi." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Selle telefoninumbriga on juba kasutaja registreeritud." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Liiga palju nurjunud sisse logimise katseid. Proovi hiljem uuesti." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "See e-posti aadress pole määratud ühelegi kasutajakontole." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "See telefoninumber pole määratud ühelegi kasutajakontole." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Su primaarne e-posti aadress peab olema kinnitatud." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Kasutajanime pole lubatud kasutada. Palun kasuta muud kasutajanime." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Sisestatud kasutajanimi või salasõna ei ole õige." #: account/adapter.py:98 msgid "Please select only one." msgstr "Palun valige ainult üks." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Uus väärtus peab erinema praegusest." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Palun ole kannatlik, sa saadad liiga palju päringuid." #: account/adapter.py:826 msgid "Use your password" msgstr "Kasuta oma salasõna" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Kasuta autentimisrakendust või koodi" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Kasuta turvavõtit" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} on märgitud kinnitatuks." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "{email} kinnituks märkimine ebaõnnestus." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Märgi valitud e-posti aadressid kinnitatuks" #: account/apps.py:11 msgid "Accounts" msgstr "Kontod" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-post" #: account/fields.py:19 msgid "Email address" msgstr "E-posti aadress" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Sisesta telefoninumber koos riigikoodiga (nt +1 USA jaoks)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefon" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Pead sisestama sama salasõna iga kord." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Salasõna" #: account/forms.py:67 msgid "Remember Me" msgstr "Jäta mind meelde" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Kasutajanimi" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Logi sisse" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Kasutajanimi, e-post või telefon" #: account/forms.py:117 msgid "Username or email" msgstr "Kasutajanimi või e-post" #: account/forms.py:119 msgid "Username or phone" msgstr "Kasutajanimi või telefon" #: account/forms.py:121 msgid "Email or phone" msgstr "E-post või telefon" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Unustasid salasõna?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-post (uuesti)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "E-posti aadress" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-post (valikuline)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Kasutajanimi (valikuline)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Pead sisetama sama e-posti aadressi iga kord." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Salasõna (uuesti)" #: account/forms.py:591 msgid "Current Password" msgstr "Praegune salasõna" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Uus salasõna" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Uus salasõna (uuesti)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Kood" #: account/models.py:23 msgid "user" msgstr "kasutaja" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "e-posti aadress" #: account/models.py:31 msgid "verified" msgstr "kinnitatud" #: account/models.py:32 msgid "primary" msgstr "primaarne" #: account/models.py:38 msgid "email addresses" msgstr "e-posti aadressid" #: account/models.py:142 msgid "created" msgstr "loodud" #: account/models.py:143 msgid "sent" msgstr "saadetud" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "võti" #: account/models.py:149 msgid "email confirmation" msgstr "kinnitus e-posti kaudu" #: account/models.py:150 msgid "email confirmations" msgstr "kinnitused e-posti kaudu" #: headless/apps.py:7 msgid "Headless" msgstr "Peata" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Vaata oma kasutaja ID-d" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Vaata oma e-posti aadressi" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Vaata oma profiili põhiandmeid" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Anna õigused" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "Metamärgid pole lubatud, kui 'Luba URI metamärgid' pole aktiveeritud." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' sisaldab rohkem kui ühte metamärki (*). Ühe URI kohta on lubatud " "ainult üks metamärk." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Metamärgid on lubatud ainult URI hostinime osas." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Autoriseerimiskood" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Seadmekood" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Kliendi volitused" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Värskendustooken" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Konfidentsiaalne" #: idp/oidc/models.py:44 msgid "Public" msgstr "Avalik" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Ulatused, mida kliendil on lubatud taotleda. Sisesta üks väärtus rea kohta, " "nt: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Kui klient ei määra ühtegi ulatust, kasutatakse neid vaikeulatusi. Sisesta " "üks väärtus rea kohta, nt: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Lubatud volituste tüüpide loend. Sisesta üks väärtus rea kohta, nt: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "Lubatud päritolu loend ristpäritoluga päringute jaoks, üks rea kohta." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Luba metamärgid (*) ümbersuunamise URI-des ja CORS-i päritoludes. Kui " "aktiveeritud, võivad URI-d sisaldada ühte tärni alamdomeenide sobitamiseks." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Lubatud vastuse tüüpide loend. Sisesta üks väärtus rea kohta, nt: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "klient" #: idp/oidc/models.py:116 msgid "clients" msgstr "kliendid" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Sa ei saa kaheastmelise autentimisega turvatud kontole lisada e-posti " "aadressi." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Sa ei saa kaheastmelist autentimist välja lülitada." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "Sa ei saa luua varukoode ilma kaheastmilise autentimiseta." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Sa ei saa välja lülitada kaheastmelist autentimist kuni sa oled oma e-posti " "aadressi kinnitanud." #: mfa/adapter.py:141 msgid "Master key" msgstr "Põhivõti" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Varuvõti" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Võti nr. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "Mitmeastmeline autentimine" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Varukoodid" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "Paroolikell" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Autentimiskood" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Paroolivaba" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Paroolivaba režiimi lubamine võimaldab sisse logida ainult selle võtmega, " "kuid kehtestab lisanõuded, nagu biomeetria või PIN-kaitse." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Selle e-posti aadressiga konto on juba olemas. Palun logi esmalt sellesse " "kontosse sisse, siis ühenda oma %s konto." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Vale tooken." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Su kontol pole salasõna määratud." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Su kontol pole kinnitatud e-posti aadresse." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Sa ei saa oma viimast kolmanda osapoole kontot lahti ühendada." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "See kolmanda osapoole konto on juba mõne teise kontoga ühendatud." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Sotsiaalkontod" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "tarnija" #: socialaccount/models.py:53 msgid "provider ID" msgstr "tarnija ID" #: socialaccount/models.py:57 msgid "name" msgstr "nimi" #: socialaccount/models.py:59 msgid "client id" msgstr "kliendi id" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "Rakenduse ID, või tarnija id" #: socialaccount/models.py:64 msgid "secret key" msgstr "salavõti" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API salajane võti, kliendi salajane võti, või tarnija salajane võti" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Võti" #: socialaccount/models.py:82 msgid "social application" msgstr "sotsiaalrakendus" #: socialaccount/models.py:83 msgid "social applications" msgstr "sotsiaalrakendused" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "viimane sisse logimine" #: socialaccount/models.py:121 msgid "date joined" msgstr "liitumise kuupäev" #: socialaccount/models.py:122 msgid "extra data" msgstr "lisaandmed" #: socialaccount/models.py:126 msgid "social account" msgstr "sotsiaalkonto" #: socialaccount/models.py:127 msgid "social accounts" msgstr "sotsiaalkontod" #: socialaccount/models.py:161 msgid "token" msgstr "tooken" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) või juurdepääsutooken (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "tookeni salavõti" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) või värskendustooken (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "aegub" #: socialaccount/models.py:175 msgid "social application token" msgstr "sotsiaalrakenduse tooken" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "sotsiaalrakenduse tookenid" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Valed profiiliandmed" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Logi sisse" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Tühista" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "Väär vastus päringutookenit tarnijalt \"%s\" hankides. Vastus oli: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Väär vastus pääsutookenit tarnijalt \"%s\" hankides." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Päringutookenit pole salvestatud \"%s\" jaoks." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Pääsutookenit pole salvestatud \"%s\" jaoks." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Puudub ligipääs \"%s\" privaatressurssidele." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Väär vastus tarnijalt \"%s\" pääsutookenit hankides." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Konto mitteaktiivne" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "See konto on mitteaktiivne." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Saatsime koodi aadressile %(recipient)s. Kood aegub varsti, seega sisesta " "see kiiresti." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Kinnita" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Taotle uut koodi" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Kinnita ligipääs" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Palun tuvasta end uuesti, et turvata oma kontot." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Muud valikud" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "E-posti kinnitamine" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Sisesta e-posti kinnituskood" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Kasuta teist e-posti aadressi" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Logi sisse" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Sisesta sisselogimiskood" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Salasõna taasseadmine" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Sisesta salasõna taasseadmise kood" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Telefoninumbri kinnitamine" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Sisesta telefoninumbri kinnituskood" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Kasuta teist telefoninumbrit" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "E-posti aadressid" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Sinu kontoga on seostatud järgnevad e-posti aadressid:" #: templates/account/email.html:25 msgid "Verified" msgstr "Kinnitatud" #: templates/account/email.html:29 msgid "Unverified" msgstr "Kinnitamata" #: templates/account/email.html:34 msgid "Primary" msgstr "Primaarne" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Muuda primaarseks" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Saada kinnitus uuesti" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Eemalda" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Lisa e-posti aadress" #: templates/account/email.html:70 msgid "Add Email" msgstr "Lisa e-post" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Oled kindel, et soovid valitud e-posti aadressi eemaldada?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Te saite selle kirja kuna kas Teie või keegi teine proovis luua konto\n" "kasutades teie e-posti aadressi:\n" "\n" "%(email)s\n" "\n" "Selle e-posti aadressiga konto on juba olemas. Juhul, kui Te olite selle\n" "unustanud, siis palun kasutage ununenud salasõna protseduuri, et saada ligi\n" "oma kontole:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Konto on juba olemas" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "%(site_name)s tervitab!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Täname, et kasutate teenust %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "Te saite selle kirja, kuna Teie kontoga tehti järgnev muudatus:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Kui Te ei tunnista seda muudatust, siis turvake oma kontot kohe. Muudatus " "kontole tuli järgnevast allikast:\n" "\n" "- IP aadress: %(ip)s\n" "- Veebilehitseja: %(user_agent)s\n" "- Kuupäev: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "" "Teie e-posti aadress muudeti. Vana e-posti aadress: %(from_email)s. Uus e-" "posti aadress: %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "E-post muudetud" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Teie e-posti aadress on kinnitatud." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "E-posti kinnitus" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Te saite selle kirja, kuna kasutaja %(user_display)s sisestas teie e-posti " "aadressi, et registreerida konto lehel %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Teie e-posti kinnituskood on alljärgnevalt esitatud. Palun sisestage see oma " "veebilehitseja avatud aknas." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Kinnitamaks, et see on õige, mine aadressile %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Palun kinnitage oma e-posti aadress" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "E-posti aadress %(deleted_email)s on Teie kontolt eemaldatud." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "E-post eemaldatud" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Teie sisselogimiskood on alljärgnevalt esitatud. Palun sisestage see oma " "veebilehitseja avatud aknas." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "Seda kirja võib eirata, kui Te ise seda toimingut ei algatanud." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Sisselogimiskood" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Teie salasõna muudeti." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Salasõna muudetud" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Teie salasõna taasseadmise kood on alljärgnevalt esitatud. Palun sisestage " "see oma veebilehitseja avatud aknas." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Salasõna taasseadmise kood" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Te saite selle kirja kuna Teie või keegi teine algatas Teie kontol parooli " "taasseadmise.\n" "Te võite seda kirja ignoreerida, kui Teie seda ei algatanud. Klõpsake " "alljärgneval lingil, et taasseada oma salasõna." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Juhuks, kui unstasite, Teie kasutajanimi on %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Salasõna taasseadmine" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Teie salasõna on taasseatud." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Teie salasõna on seatud." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Salasõna seatud" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Te saite selle kirja kuna Teie, või keegi teine, proovis pääseda ligi Teie " "kontole e-posti aadressiga %(email)s. Ent meil pole ühtegi kirjet sellise e-" "posti aadressiga kontost andmebaasis." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Kui see olite Teie, siis saate registreerida konto kasutades alljärgnevat " "linki." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Tundmatu konto" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "E-posti aadress" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Praegune e-post" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Muudatus" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Su e-posti aadress on endiselt kinnitamata." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Tühista muudatus" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Uus e-post" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Muuda e-posti" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Kinnita e-posti aadress" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Palun kinnita, et %(email)s on konto " "%(user_display)s e-posti aadress." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Ei saa kinnitada aadressi %(email)s kuna see on juba teise kontoga " "kinnitatud." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "See e-posti aadressi kinnituslink aegus või on vale. Palun taotlege uus e-posti aadressi kinnitamine." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Kui sa pole veel kontot loonud, siis palun esmalt " "%(link)sregistreeruge%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Logi sisse pääsuvõtmega" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Saada mulle sisselogimiskood" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Logi välja" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Oled kindel, et soovid välja logida?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Primaarset e-posti aadressi (%(email)s) ei saa eemaldada." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Kinnituskiri saadeti aadressile %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Sa kinnitasid e-posti aadressi %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "E-posti aadress %(email)s eemaldatud." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Logisid edukalt sisse kasutajana %(name)s" #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Sa logisid välja." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Sisselogimiskood saadeti aadressile %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Salasõna muudeti edukalt." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Salasõna seati edukalt." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Kinnituskood saadeti numbrile %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Oled kinnitanud telefoninumbri %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Primaarne e-posti aadress on nüüd seatud." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Muuda salasõna" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Unustasid salasõna?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Unustasid oma salasõna? Sisesta allpool oma e-posti aadress, ning me saadame " "sulle kirja, mille abil saad selle taasseada." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Taassea minu salasõna" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Palun võta meiega ühendust, kui näed vaeva oma salasõna taasseadmisega." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Saatsime teile e-kirja. Kui sa pole seda kätte saanud, siis vaata rämpsposti " "kausta. Muul juhul, võta meiega ühendust kui sa ei saa seda mõne minuti " "jooksul kätte." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Vale tooken" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Salasõna taasseadmise link oli vale. On võimalik, et seda oli juba " "kasutatud. Palun taotle uus salasõna " "taasseadmine." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Su salasõna on nüüd muudetud." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Sea salasõna" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Muuda telefoni" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Praegune telefon" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Teie telefoninumber on endiselt kinnitamata." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Muuda telefoni" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Sisesta oma salasõna:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Sa saad spetsiaalse koodi paroolivabaks sisselogimiseks." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Taotle koodi" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Muud sisse logimise võimalused" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Registreerimine" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Registreeri" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Konto juba olemas? Palun %(link)slogi sisse%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Registreeru pääsuvõtmega" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Pääsuvõtmega registreerumine" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Muud valikud" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Sisselogimine peatatud" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Sisselogimine on parajasti peatatud. Palume vabandust!" #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Teavitus" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Sa oled juba sisse logitud kasutajana %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Hoiatus:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Su kontol pole parajasti ühtegi e-posti aadressi. Soovitame kindlasti seada " "e-posti aadressi, et sa saaksid saada teavitusi, taasseada salasõna jne." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Kinnita oma e-posti aadress" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Saatsime sulle kinnituseks e-kirja. Järgi kirjas olevat linki, et lõpetada " "registreerimine. Kui sa ei näe kinnituskirja postkastis, kontrolli " "rämpsposti kausta. Palun võta meiega ühendust, kui sa ei saa kinnituskirja " "mõne minuti jooksul." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "See osa lehest nõuab, et me kinnitaksime sinu sisestatud andmeid. Selle " "tarbeks nõuame kinnitust, et\n" "oled oma e-posti aadressi omanik. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Saatsime teile kinnituseks e-kirja. Palun klõpsa kirja sees olevale lingile. " "Kui sa ei näe kinnituskirja oma postkastis, siis vaata rämpsposti kausta. " "Vastasel juhul,\n" "võta meiega ühendust kui sa ei saa kirja mõne minuti jooksul." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Märkus: saad ikka oma e-posti " "aadressi muuta." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Sõnumid" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menüü:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Konto sidemed" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Kaheastmeline autentimine" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sessioonid" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Autoriseeri" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s soovib ligipääsu teie %(site_name)s kontole." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Sisesta seadmekood" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Sisesta oma seadmel kuvatav kood." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Jätka" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Kinnita seade" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Palun kinnitage oma %(client_name)s kuvatav kood selle seadme " "autoriseerimiseks." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Keeldu" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Seade autoriseeritud" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Autoriseerisite edukalt oma %(client_name)s seadme." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Seade keelatud" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Teie %(client_name)s seadme autoriseerimine on keelatud." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Viga" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Jää sisselogituks" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Su konto on turvatud kaheastmelise autentimisega. Palun sisesta " "autentimiskood:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "Uus hulk kaheastmelise autentimise varukoode on loodud." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Uued varukoodid loodud" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Autentimisrakendus seadistatud" #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Autentimisrakendus seadistatud" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Autentimisrakendus eemaldatud" #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Autentimisrakendus eemaldatud" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Uus turvavõti on lisatud." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Turvavõti lisatud" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Turvavõti on eemaldatud." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Turvavõti eemaldatud" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Autentimisrakendus" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Autentimine kasutades autentimisrakendust on seadistatud." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Ühtegi autentimisrakendust pole seadistatud." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Eemalda" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Seadista" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Turvavõtmed" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Olete lisanud %(count)s turvavõtme." msgstr[1] "Olete lisanud %(count)s turvavõtit." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Ühtegi turvavõtit pole lisatud." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Halda" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Lisa" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Varukoodid" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "Saadaval on %(unused_count)s taastamiskoodi %(total_count)s-st." msgstr[1] "Saadaval on %(unused_count)s taastamiskoodi %(total_count)s-st." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Varukoode pole seadistatud" #: templates/mfa/index.html:96 msgid "View" msgstr "Vaata" #: templates/mfa/index.html:102 msgid "Download" msgstr "Lae alla" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Loo" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Uus hulk varukoode on loodud." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Turvavõti lisatud." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Turvavõti eemaldatud." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Sisesta autentimisrakenduse kood:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Järgnevalt loote uue hulga varukoode oma kontole." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "See tegevus muudab olemasolevad varukoodid kehtetuks." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Oled kindel?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Kasutamata varukoodid" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Lae varukoodid alla" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Loo uued varukoodid" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Seadista autentimisrakendus" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Et turvata oma kontot kaheastmelise autentimisega, skänni alljärgnev QR-kood " "oma autentimisrakendusega. Seejärel, sisesta rakenduse loodud kinnituskood " "all olevasse lahtrisse." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Autentimisrakenduse saladus" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Hoiustades selle saladuse saad seda kasutada, et taaspaigaldada oma " "autentimisrakendust tulevikus." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Eemalda autentimisrakendus" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Järgneva tegevusega eemaldad autentimisrakendusega seotud autentimise. Kas " "oled kindel?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Usalda seda brauserit?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Kui otsustad seda brauserit usaldada, ei küsita sinult järgmisel " "sisselogimisel kinnituskoodi." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Usalda %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Ära usalda" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Lisa turvavõti" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Eemalda turvavõti" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Oled kindel, et soovid selle turvavõtme eemaldada?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Kasutus" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Pääsuvõti" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Turvavõti" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "See võti ei näita, kas tegemist on pääsuvõtmega." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Määramata" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Lisatud %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Viimati kasutatud %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Muuda" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Muuda turvavõtit" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Salvesta" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Loo pääsuvõti" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Oled loomas pääsuvõtit oma kontole. Kuna saad hiljem lisada täiendavaid " "võtmeid, võid kasutada kirjeldavat nime, et neid eristada." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Loo" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "See funktsioon nõuab JavaScripti." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Kolmanda osapoolega sisse logimine nurjus" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "Kolmanda osapoole konto kaudu sisse logimise ajal tekkis viga." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Saad sisse logida oma kontosse kasutades mistahes järgnevatest kolmanda " "osapoole kontodest:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Su kontoga pole parajasti seotud ühtegi kolmanda osapoole kontot." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Lisa kolmanda osapoole konto" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Kolmanda osapoole %(provider)s konto on Teie kontoga ühendatud." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Kolmanda osapoole konto ühendatud" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" "Seos kolmanda osapoole %(provider)s konto ja Teie konto vahel on eemaldatud." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Kolmanda osapoole konto eemaldatud" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Ühenda %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "" "Järgneva tegevusega ühendate uue kolmanda osapoole konto pakkujalt " "%(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Logi sisse %(provider)s kaudu" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" "Järgneva tegevusega logite sisse kasutades kolmanda osapoole %(provider)s " "kontot." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Sisse logimine tühistatud" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Otsustasid meie lehte sisse logimise tühistada.Kui see oli eksimus, palun " "mine sisse logima." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Kolmanda osapoole konto on ühendatud." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Seos kolmanda osapoole kontoga eemaldati." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Järgneva tegevusega kasutate pakkuja %(provider_name)s kontot sisse " "logimiseks lehele\n" "%(site_name)s. Viimase sammuna, palun täitke ankeet:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Või kasuta kolmandat osapoolt" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Kõigist teistest sessioonidest välja logitud." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Alustati" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP aadress" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Veebilehitseja" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Viimati nähtud" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Praegune" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Logi teistest sessioonidest välja" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Kasutajasessioonid" #: usersessions/models.py:94 msgid "session key" msgstr "sessioonivõti" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Konto sidemed" ================================================ FILE: allauth/locale/eu/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: django-allauth\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2024-10-31 18:31+0000\n" "Last-Translator: Alain EIguren \n" "Language-Team: Basque \n" "Language: eu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.8.2-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Kontu hau ez dago aktibo une honetan." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Ezin duzu zure helbide elektroniko nagusia ezabatu." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Helbide elektroniko hau dagoeneko kontu honi lotuta dago." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Sartutako helbide elektronikoa eta/edo pasahitza ez dira zuzenak." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Sartutako telefono-zenbakia eta/edo pasahitza ez dira zuzenak." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "" "Erabiltzaile batek kontu bat sortu du jada helbide elektroniko honekin." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Mesedez idatzi zure oraingo pasahitza." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Kode okerra." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Pasahitz okerra." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Gako baliogabea edo iraungitakoa." #: account/adapter.py:79 msgid "Invalid login." msgstr "Saio-hasiera baliogabea." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Pasahitza berrezartzeko \"token\"-a baliogabea da." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Ezin dituzu %d email helbide baino gehiago erabili." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Erabiltzaile batek kontu bat sortu du jada telefono-zenbaki honekin." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Huts egite gehiegi saioa hasterakoan. Saiatu berriro beranduago." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Helbide elektronikoa ez dago erabiltzaile-kontu bati esleituta." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Telefono-zenbakia ez dago erabiltzaile-kontu bati esleituta." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Zure email nagusiak egiaztatuta egon behar du." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "" "Erabiltzaile izen hau ezin da erabili. Aukeratu beste erabiltzaile izen bat." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Sartutako erabiltzailea eta/edo pasahitza ez dira zuzenak." #: account/adapter.py:98 msgid "Please select only one." msgstr "Mesedez, hautatu bakarra." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Balio berriak oraingotik desberdina izan behar du." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Izan pazientzia, eskaera gehiegi bidaltzen ari zara." #: account/adapter.py:826 msgid "Use your password" msgstr "Erabili zure pasahitza" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Erabili autentifikazio-aplikazioa edo kodea" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Erabili segurtasun-gako bat" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} egiaztatu bezala markatu da." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Ezin izan da {email} egiaztatu bezala markatu." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Markatu hautatutako helbide elektronikoak egiaztatu bezala" #: account/apps.py:11 msgid "Accounts" msgstr "Kontuak" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "Emaila" #: account/fields.py:19 msgid "Email address" msgstr "Helbide elektronikoa" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Sartu telefono-zenbaki bat herrialde-kodearekin (adib. +34 Espainia)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefonoa" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Pasahitz berdina idatzi behar duzu aldi bakoitzean." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Pasahitza" #: account/forms.py:67 msgid "Remember Me" msgstr "Gogora nazazue" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Erabiltzailea" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Saioa hasi" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Erabiltzailea, emaila edo telefonoa" #: account/forms.py:117 msgid "Username or email" msgstr "Erabiltzailea edo emaila" #: account/forms.py:119 msgid "Username or phone" msgstr "Erabiltzailea edo telefonoa" #: account/forms.py:121 msgid "Email or phone" msgstr "Emaila edo telefonoa" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Pasahitza ahaztu duzu?" #: account/forms.py:287 msgid "Email (again)" msgstr "Emaila (berriro)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Helbide elektronikoaren egiaztapena" #: account/forms.py:302 msgid "Email (optional)" msgstr "Emaila (hautazkoa)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Erabiltzailea (hautazkoa)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Email berdina idatzi behar duzu aldi bakoitzean." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Pasahitza (berriro)" #: account/forms.py:591 msgid "Current Password" msgstr "Oraingo pasahitza" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Pasahitz berria" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Pasahitz berria (berriro)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Kodea" #: account/models.py:23 msgid "user" msgstr "erabiltzailea" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "helbide elektronikoa" #: account/models.py:31 msgid "verified" msgstr "egiaztatuta" #: account/models.py:32 msgid "primary" msgstr "nagusia" #: account/models.py:38 msgid "email addresses" msgstr "helbide elektronikoak" #: account/models.py:142 msgid "created" msgstr "sortuta" #: account/models.py:143 msgid "sent" msgstr "bidalita" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "giltza" #: account/models.py:149 msgid "email confirmation" msgstr "email egiaztapena" #: account/models.py:150 msgid "email confirmations" msgstr "email egiaztapenak" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Ikusi zure erabiltzaile-IDa" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Ikusi zure helbide elektronikoa" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Ikusi zure oinarrizko profileko informazioa" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Baimenak eman" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Komodinak (*) ez daude baimenduta 'Baimendu URI komodinak' aktibatuta ez " "badago." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "'{}' URIak komodin (*) bat baino gehiago dauka. URI bakoitzeko komodin " "bakarra onartzen da." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Komodinak URIaren ostalari-izenaren zatian soilik onartzen dira." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Baimen-kodea" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Gailu-kodea" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Bezeroaren kredentzialak" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Berritzeko tokena" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Konfidentziala" #: idp/oidc/models.py:44 msgid "Public" msgstr "Publikoa" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Bezeroak eskatu ditzakeen esparruak. Eman balio bat lerro bakoitzeko, adib.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Bezeroak esparrurik zehazten ez badu, esparru lehenetsi hauek erabiltzen " "dira. Eman balio bat lerro bakoitzeko, adib.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Baimendutako baimen-moten zerrenda. Eman balio bat lerro bakoitzeko, adib.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Jatorri gurutzatuko eskaeren baimendutako jatorrien zerrenda, bat lerro " "bakoitzeko." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Baimendu komodinak (*) birbideratze-URIetan eta CORS jatorrietan. Aktibatuta " "dagoenean, URIek izartxo bakarra eduki dezakete azpidomeinuekin bat " "etortzeko." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Baimendutako erantzun-moten zerrenda. Eman balio bat lerro bakoitzeko, " "adib.: code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "bezeroa" #: idp/oidc/models.py:116 msgid "clients" msgstr "bezeroak" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Ezin duzu helbide elektroniko bat gehitu bi faktoreko autentifikazioarekin " "babestutako kontu batean." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Ezin duzu bi faktoreko autentifikazioa desaktibatu." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Ezin dituzu berreskuratze-kodeak sortu bi faktoreko autentifikazioa " "aktibatuta izan gabe." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Ezin duzu bi faktoreko autentifikazioa aktibatu zure helbide elektronikoa " "egiaztatu arte." #: mfa/adapter.py:141 msgid "Master key" msgstr "Gako nagusia" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Babeskopia-gakoa" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "{number}. gakoa" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Berreskuratze-kodeak" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP autentifikatzailea" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Autentifikazio-kodea" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Pasahitzarik gabe" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Pasahitzarik gabeko funtzionamendua aktibatzeak gako honekin soilik saioa " "hastea ahalbidetzen dizu, baina baldintza gehigarriak ezartzen ditu, hala " "nola biometria edo PIN bidezko babesa." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Kontu bat existitzen da jada helbide elektroniko honekin. Mesedez, hasi " "saioa kontu horretan lehenik, eta gero lotu zure %s kontua." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Token baliogabea." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Zure kontuak ez du pasahitzik zehaztuta." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Zure kontuak ez du egiaztatutako emailik." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Ezin duzu zure azken hirugarrenen kontua deskonektatu." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Hirugarrenen kontua dagoeneko beste kontu bati lotuta dago." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Sare sozial kontuak" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "zerbitzua" #: socialaccount/models.py:53 msgid "provider ID" msgstr "hornitzailearen IDa" #: socialaccount/models.py:57 msgid "name" msgstr "izena" #: socialaccount/models.py:59 msgid "client id" msgstr "client id" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "Aplikazioaren ID-a, edo \"consumer key\"-a" #: socialaccount/models.py:64 msgid "secret key" msgstr "\"secret key\"-a" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "\"API secret\"-a, \"client secret\"-a edo \"consumer secret\"-a" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Giltza" #: socialaccount/models.py:82 msgid "social application" msgstr "aplikazio soziala" #: socialaccount/models.py:83 msgid "social applications" msgstr "aplikazio sozialak" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "azken logina" #: socialaccount/models.py:121 msgid "date joined" msgstr "erregistro eguna" #: socialaccount/models.py:122 msgid "extra data" msgstr "datu gehigarriak" #: socialaccount/models.py:126 msgid "social account" msgstr "sare sozial kontua" #: socialaccount/models.py:127 msgid "social accounts" msgstr "sare sozial kontuak" #: socialaccount/models.py:161 msgid "token" msgstr "\"token\"-a" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\"-a (OAuth1) edo \"access token\"-a (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "\"token secret\"-a" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\"-a (OAuth1) edo \"refresh token\"-a (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "iraungitze data" #: socialaccount/models.py:175 msgid "social application token" msgstr "aplikazio sozial \"token\"-a" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "aplikazio sozial \"token\"-ak" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Profil datu baliogabeak" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Saioa hasi" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Utzi" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Erantzun baliogabea \"%s\"-(e)tik eskaera-tokena eskuratzean. Erantzuna hau " "izan zen: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Erantzun baliogabea \"%s\"-tik \"access token\"-a eskuratzean." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Ez dago \"request token\"-ik gordeta \"%s\"-entzat." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Ez dago \"access token\"-ik gordeta \"%s\"-entzat." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Ez duzu baliabide pribatuetara sarbiderik: \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Erantzun baliogabea \"%s\"-tik \"request token\"-a eskuratzean." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Kontu ez aktiboa" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Kontu hau ez dago aktiboa." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Kode bat bidali dugu %(recipient)s helbidera. Kodea laster iraungiko da, " "beraz, sartu ahalik eta lasterren." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Egiaztatu" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Kode berria eskatu" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Sarbidea baieztatu" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Mesedez, berriz autentifikatu zure kontua babesteko." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Beste aukera batzuk" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Email egiaztapena" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Sartu emailaren egiaztapen-kodea" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Erabili beste helbide elektroniko bat" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Saioa hasi" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Sartu saio-hasierako kodea" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Pasahitza berrezarri" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Sartu pasahitza berrezartzeko kodea" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Telefono egiaztapena" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Sartu telefonoaren egiaztapen-kodea" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Erabili beste telefono-zenbaki bat" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Helbide elektronikoak" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Helbide elektroniko hauek zure kontuari lotuta daude:" #: templates/account/email.html:25 msgid "Verified" msgstr "Egiaztatuta" #: templates/account/email.html:29 msgid "Unverified" msgstr "Egiaztatu gabe" #: templates/account/email.html:34 msgid "Primary" msgstr "Nagusia" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Nagusia egin" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Egiaztapen emaila berbidali" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Ezabatu" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Helbide elektronikoa gehitu" #: templates/account/email.html:70 msgid "Add Email" msgstr "Emaila gehitu" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Ziur al zaude aukeratutako helbide elektronikoa ezabatu nahi duzula?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Email hau jaso duzu zuk edo beste norbaitek kontu bat sortzeko saiakera egin " "duelako\n" "helbide elektroniko honekin:\n" "\n" "%(email)s\n" "\n" "Hala ere, helbide elektroniko hori erabiltzen duen kontu bat dagoeneko " "existitzen da. Hau\n" "ahaztu baduzu, erabili pasahitza berreskuratzeko prozedura zure kontua\n" "berreskuratzeko:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Kontua dagoeneko existitzen da" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Kaixo %(site_name)s webgunetik!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Mila esker %(site_name)s webgunea erabiltzeagatik!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "Mezu hau jaso duzu zure kontuan honako aldaketa egin delako:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Aldaketa hau ezagutzen ez baduzu, hartu berehala segurtasun-neurriak. Zure " "kontuaren aldaketa hemendik dator:\n" "\n" "- IP helbidea: %(ip)s\n" "- Nabigatzailea: %(user_agent)s\n" "- Data: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Zure emaila %(from_email)s-tik %(to_email)s-era aldatu da." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Emaila aldatu da" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Zure emaila baieztatu da." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Email baieztapena" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Email hau jaso duzu %(user_display)s erabiltzaileak zure helbide " "elektronikoa eman duelako %(site_domain)s orrialdean kontu bat " "erregistratzeko." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Zure emailaren egiaztapen-kodea behean agertzen da. Mesedez, sartu zure " "nabigatzaileko leiho irekian." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Hau zuzena dela baieztatzeko, joan %(activate_url)s helbidera." #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Mesedez egiaztatu zure helbide elektronikoa" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "%(deleted_email)s helbide elektronikoa zure kontutik kendu da." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Emaila kendu da" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Zure saio-hasierako kodea behean agertzen da. Mesedez, sartu zure " "nabigatzaileko leiho irekian." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "Mezu hau alde batera utzi dezakezu ekintza hau zuk hasi ez baduzu." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Saio-hasierako kodea" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Zure pasahitza aldatu da." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Pasahitza aldatu da" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Zure pasahitza berrezartzeko kodea behean agertzen da. Mesedez, sartu zure " "nabigatzaileko leiho irekian." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Pasahitza berrezartzeko kodea" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Email hau jaso duzu zuk edo beste norbaitek pasahitza berrezartzeko eskaera " "egin duelako zure kontuarentzat.\n" "Eskaera zuk egin ez baduzu, mezu hau alde batera utzi dezakezu. Egin klik " "beheko estekan zure pasahitza berrezartzeko." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Ahaztu baduzu, zure erabiltzaile izena %(username)s da." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Pasahitza berrezartzeko emaila" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Zure pasahitza berrezarri da." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Zure pasahitza ezarri da." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Pasahitza ezarri da" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Email hau jaso duzu zuk, edo beste norbaitek, %(email)s emailarekin kontu " "bat atzitzen saiatu delako. Hala ere, ez dugu kontu horren erregistrorik " "gure datu-basean." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "Zuk izan bazara, kontu bat sortu dezakezu beheko esteka erabiliz." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Kontu ezezaguna" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Helbide elektronikoa" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Oraingo emaila" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Aldatzen" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Zure helbide elektronikoa oraindik egiaztatzeko dago." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Aldaketa bertan behera utzi" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Aldatu hona" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Emaila aldatu" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Helbide elektronikoa egiaztatu" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Mesedez, baieztatu %(email)s " "%(user_display)s erabiltzailearen helbide elektronikoa dela." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Ezin da %(email)s baieztatu, dagoeneko beste kontu batek baieztatu duelako." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Email baieztapen-esteka hau iraungi da edo baliogabea da. Mesedez, eskatu email baieztapen-eskaera berri bat." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Oraindik kontu bat sortu ez baduzu, mesedez %(link)ssortu kontu " "bat%(end_link)s lehenik." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Hasi saioa pasahitz-gako batekin" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Bidali saio-hasierako kode bat" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Saioa amaitu" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Ziur al zaude saioa amaitu nahi duzula?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Ezin duzu zure helbide elektroniko nagusia ezabatu (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Egiaztapen emaila bidali da %(email)s helbidera." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "%(email)s emaila egiaztatu duzu." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "%(email)s helbide elektronikoa ezabatu da." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "%(name)s bezala hasi duzu saioa." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Saioa amaitu duzu." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Saio-hasierako kode bat bidali da %(recipient)s helbidera." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Pasahitza behar bezala aldatu da." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Pasahitza behar bezala zehaztu da." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Egiaztapen-kode bat bidali da %(phone)s zenbakira." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "%(phone)s telefono-zenbakia egiaztatu duzu." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Helbide elektroniko nagusia zehaztu da." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Pasahitza aldatu" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Pasahitza ahaztu duzu?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Zure pasahitza ahaztu al duzu? Idatzi zure helbide elektronikoa behean eta " "pasahitza berrezartzeko aukera emango dizun email bat bidaliko dizugu." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Nire pasahitza berrezarri" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Mesedez jarri gurekin kontaktuan zure pasahitza berrezartzeko arazorik " "baduzu." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Email bat bidali dizugu. Jaso ez baduzu, egiaztatu zure spam karpeta. " "Bestela, jarri gurekin harremanetan minutu batzuetan jasotzen ez baduzu." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Token baliogabea" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Pasahitza berrezartzeko esteka baliogabea da, beharbada lehendik ere erabili " "delako. Mesedez eskatu pasahitza " "berrezartzeko email berri bat." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Zure pasahitza aldatuta dago orain." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Pasahitza zehaztu" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Telefonoa aldatu" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Oraingo telefonoa" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Zure telefono-zenbakia oraindik egiaztatzeko dago." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Telefonoa aldatu" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Sartu zure pasahitza:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Kode berezi bat jasoko duzu pasahitzarik gabeko saio-hasierarako." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Kodea eskatu" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Saio-hasierako beste aukera batzuk" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Kontua sortu" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Kontua sortu" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Lehendik kontu bat sortua duzu? %(link)sSaioa hasi%(end_link)s orduan." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Erregistratu pasahitz-gako bat erabiliz" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Pasahitz-gakoaren bidez kontua sortu" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Beste aukera batzuk" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Ezin da konturik sortu iada" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Sentitzen dugu baina ezin da kontu berririk sortu." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Oharra" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Dagoeneko saioa hasita duzu %(user_display)s bezala." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Adi:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Oraingoz ez duzu helbide elektronikorik ezarri. Helbide elektroniko bat " "gehitu beharko zenuke jakinarazpenak jaso ahal izateko, pasahitza " "berrezartzeko, etab." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Zure helbide elektronikoa egiaztatu" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Email bat bidali dizugu egiaztapenerako. Jarraitu emandako esteka " "erregistratzeko prozesua amaitzeko. Egiaztapen-emaila zure sarrera-ontzi " "nagusian ikusten ez baduzu, egiaztatu spam karpeta. Mesedez, jarri gurekin " "harremanetan minutu batzuetan egiaztapen-emaila jasotzen ez baduzu." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Webguneko atal honek zuk diozuna zarela egiaztatzea\n" "eskatzen digu. Honetarako, zure helbide elektronikoaren\n" "jabetza egiaztatzea beharrezkoa da. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Email bat bidali dizugu\n" "egiaztapenerako. Mesedez, egin klik emailean dagoen estekan. Egiaztapen-" "emaila zure sarrera-ontzi nagusian ikusten ez baduzu, egiaztatu spam " "karpeta. Bestela,\n" "jarri gurekin harremanetan minutu batzuetan jasotzen ez baduzu." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Oharra: oraindik zure helbide " "elektronikoa aldatu dezakezu." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Mezuak:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menua:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Lotutako kontuak" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Bi faktoreko autentifikazioa" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Saioak" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Baimendu" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s-ek zure %(site_name)s kontua atzitu nahi du." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Sartu gailuaren kodea" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Sartu zure gailuan agertzen den kodea." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Jarraitu" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Gailua baieztatu" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Mesedez, baieztatu zure %(client_name)s-en agertzen den kodea gailu hau " "baimentzeko." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Ukatu" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Gailua baimenduta" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Zure %(client_name)s gailua behar bezala baimendu duzu." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Gailua ukatua" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Zure %(client_name)s gailuaren baimena ukatu da." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Errorea" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Saioa hasita mantendu" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Zure kontua bi faktoreko autentifikazioarekin babestuta dago. Mesedez, sartu " "autentifikazio-kode bat:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Bi faktoreko autentifikazioaren berreskuratze-kode sorta berri bat sortu da." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Berreskuratze-kode berriak sortu dira" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Autentifikazio-aplikazioa aktibatu da." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Autentifikazio-aplikazioa aktibatu da" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Autentifikazio-aplikazioa desaktibatu da." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Autentifikazio-aplikazioa desaktibatu da" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Segurtasun-gako berri bat gehitu da." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Segurtasun-gakoa gehitu da" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Segurtasun-gako bat kendu da." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Segurtasun-gakoa kendu da" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Autentifikazio-aplikazioa" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Autentifikazio-aplikazioaren bidezko autentifikazioa aktibatuta dago." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Autentifikazio-aplikazioa ez dago aktibo." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Desaktibatu" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Aktibatu" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Segurtasun-gakoak" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "%(count)s segurtasun-gako gehitu duzu." msgstr[1] "%(count)s segurtasun-gako gehitu dituzu." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Ez da segurtasun-gakorik gehitu." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Kudeatu" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Gehitu" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Berreskuratze-kodeak" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "%(total_count)s berreskuratze-kodetik %(unused_count)s dago erabilgarri." msgstr[1] "" "%(total_count)s berreskuratze-kodetik %(unused_count)s daude erabilgarri." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Ez da berreskuratze-koderik ezarri." #: templates/mfa/index.html:96 msgid "View" msgstr "Ikusi" #: templates/mfa/index.html:102 msgid "Download" msgstr "Deskargatu" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Sortu" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Berreskuratze-kode sorta berri bat sortu da." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Segurtasun-gakoa gehitu da." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Segurtasun-gakoa kendu da." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Sartu autentifikazio-kode bat:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Zure konturako berreskuratze-kode sorta berri bat sortzear zaude." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Ekintza honek zure oraingo kodeak baliogabetuko ditu." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Ziur al zaude?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Erabili gabeko kodeak" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Kodeak deskargatu" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Kode berriak sortu" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Autentifikazio-aplikazioa aktibatu" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Zure kontua bi faktoreko autentifikazioarekin babesteko, eskaneatu beheko QR " "kodea zure autentifikazio-aplikazioarekin. Ondoren, sartu aplikazioak " "sortutako egiaztapen-kodea behean." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Autentifikatzailearen sekretua" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Sekretu hau gorde dezakezu eta geroago zure autentifikazio-aplikazioa " "berrinstalatzeko erabili." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Autentifikazio-aplikazioa desaktibatu" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Autentifikazio-aplikazioan oinarritutako autentifikazioa desaktibatzear " "zaude. Ziur al zaude?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Nabigatzaile honetan fidatu?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Nabigatzaile honetan fidatzea aukeratzen baduzu, ez zaizu egiaztapen-koderik " "eskatuko hurrengo saioa hasten duzunean." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Fidatu %(period)s-rako" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Ez fidatu" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Segurtasun-gakoa gehitu" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Segurtasun-gakoa kendu" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Ziur al zaude segurtasun-gako hau kendu nahi duzula?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Erabilera" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Pasahitz-gakoa" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Segurtasun-gakoa" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Gako honek ez du adierazten pasahitz-gakoa den ala ez." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Zehaztu gabea" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "%(created_at)s-en gehitua" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Azkenekoz %(last_used)s-en erabilia" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Editatu" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Segurtasun-gakoa editatu" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Gorde" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Pasahitz-gakoa sortu" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Zure konturako pasahitz-gako bat sortzear zaude. Geroago gako gehiago gehi " "ditzakezunez, izen deskribatzaile bat erabil dezakezu gakoak bereizteko." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Sortu" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Funtzionalitate honek JavaScript behar du." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Hirugarrenen bidezko saio-hasieraren errorea" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Errore bat gertatu da zure hirugarrenen kontua erabiliz saioa hasteko " "ahaleginean." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Ondorengo hirugarrenen kontu hauetako edozein erabili dezakezu zure kontuan " "sartzeko:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Oraingoz ez duzu hirugarrenen konturik lotu kontu honekin." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Hirugarrenen kontu bat gehitu" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "%(provider)s-eko hirugarrenen kontu bat zure kontuarekin konektatu da." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Hirugarrenen kontua konektatu da" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "%(provider)s-eko hirugarrenen kontu bat zure kontutik deskonektatu da." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Hirugarrenen kontua deskonektatu da" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "%(provider)s konektatu" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "%(provider)s-eko hirugarrenen kontu berri bat konektatzear zaude." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "%(provider)s bidez saioa hasi" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "%(provider)s-eko hirugarrenen kontu bat erabiliz saioa hastear zaude." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Baliogabetutako logina" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Lotutako kontu batekin saioa hasteko saiakera bertan behera utzi duzu. " "Oharkabean gertatu bada, mesedez saioa hasi " "berriro." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Hirugarrenen kontua konektatu da." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Hirugarrenen kontua deskonektatu da." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Zure %(provider_name)s kontua erabiltzear zaude %(site_name)s\n" "webgunean saioa hasteko. Azken pausu bezala, mesedez osa ezazu\n" "formulario hau:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Edo erabili hirugarren bat" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Beste saio guztietatik irten da." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Hasiera-data" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP helbidea" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Nabigatzailea" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Azkenekoz ikusita" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Oraingoa" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Beste saioetatik irten" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Erabiltzailearen saioak" #: usersessions/models.py:94 msgid "session key" msgstr "saio-gakoa" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Lotutako kontuak" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Pasahitzak gutxienez {0} karaktere izan behar ditu." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Email hau jaso duzu zuk edo beste norbaitek pasahitza berrezartzeko " #~ "eskaera egin duelako zure kontuarentzat.\n" #~ "Eskaera zuk egin ez baduzu mezu hau alde batera utzi dezakezu. Edo egin " #~ "klik ondorengo estekan zure pasahitza berrezartzeko." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "Helbide elektroniko hauek zure kontuari lotuta daude:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Helbide elektronikoa egiaztatu" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Mesedez hasi saioa lotutako sare sozial kontu bat\n" #~ "erabiliz, edo %(link)ssortu kontu bat%(end_link)s\n" #~ "%(site_name)s webgunean eta saioa hasi hemen:" #~ msgid "or" #~ msgstr "edo" #~ msgid "change password" #~ msgstr "pasahitza aldatu" #~ msgid "OpenID Sign In" #~ msgstr "OpenID-rekin sartu" #~ msgid "This email address is already associated with another account." #~ msgstr "Helbide elektroniko hau dagoeneko beste kontu bati lotuta dago." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Email bat bidali dizugu. Mesedez jarri gurekin kontaktuan hurrengo " #~ "minutuetan jasotzen ez baduzu." ================================================ FILE: allauth/locale/fa/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-10-22 13:02+0000\n" "Last-Translator: eddipa \n" "Language-Team: Persian \n" "Language: fa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.14-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "اکنون این حساب غیرفعال است." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "امکان حذف نشانه ایمیل اصلی (%(email)s) مقدور نمی باشد." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "این نشانی ایمیل ازقبل به این حساب وصل شده." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "نشانی ایمیل یا گذرواژه نادرست است." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "شماره تلفن و/یا گذرواژه وارد شده نادرست است." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "یک کاربر ازقبل با این نشانی ایمیل ثبت شده است." #: account/adapter.py:75 msgid "Please type your current password." msgstr "لطفا گذرواژه کنونی‌‌ات را وارد کن." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "کد اشتباه است." #: account/adapter.py:77 msgid "Incorrect password." msgstr "رمز عبور اشتباه است." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "کلید نامعتبر است یا منقضی شده است." #: account/adapter.py:79 msgid "Invalid login." msgstr "اطلاعات ورود نادرست است." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "توکن بازنشانی گذرواژه نامعتبر است." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "نمی توانید بیش از %d تعداد آدرس ایمیل اضافه کنید." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "یک کاربر ازقبل با این شماره تلفن ثبت شده است." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "تعداد تلاش های ناموفق بیش از حد مجاز شده است. لطفا بعدا تلاش کنید." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "این نشانی ایمیل به هیچ حساب کاربری‌ای متصل نشده است." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "این شماره تلفن به هیچ حساب کاربری‌ای متصل نشده است." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "نشانی ایمیل اصلی‌ شما باید تایید شده باشد." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "نام‌کاربری قابل استفاده نیست. لطفا از نام‌کاربری دیگری استفاده کنید." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "نام‌کاربری یا گذرواژه وارد شده نادرست است." #: account/adapter.py:98 msgid "Please select only one." msgstr "لطفا یکی را انتخواب کنید" #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "عبارت تازه بایستی با عبارت حاضر متفاوت باشد." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "لطفا صبور باشید، تعداد درخواست های شما بیش از حد مجاز است." #: account/adapter.py:826 msgid "Use your password" msgstr "از گذرواژه خود استفاده کنید" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "از برنامه تایید دومرحله ای یا کد استفاده کنید" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "از کلید امنیتی استفاده کنید" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "ایمیل {email} به عنوان تایید شده نشانه گذاری شد." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "علامت‌گذاری {email} به عنوان تایید شده ناموفق بود." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "استفاده از ایمیل های انتخابی به عنوان آدرس های تایید شده" #: account/apps.py:11 msgid "Accounts" msgstr "حساب‌ها" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "ایمیل" #: account/fields.py:19 msgid "Email address" msgstr "نشانی ایمیل" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "یک شماره تلفن شامل کد کشور وارد کنید (مثلا +1 برای آمریکا)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "تلفن" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "هربار باید گذرواژه‌ی یکسانی را وارد کنی." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "گذرواژه" #: account/forms.py:67 msgid "Remember Me" msgstr "مرا به خاطر بسپار" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "نام‌کاربری" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "ورود" #: account/forms.py:115 msgid "Username, email or phone" msgstr "نام‌کاربری، ایمیل یا تلفن" #: account/forms.py:117 msgid "Username or email" msgstr "نام‌کاربری یا ایمیل" #: account/forms.py:119 msgid "Username or phone" msgstr "نام‌کاربری یا تلفن" #: account/forms.py:121 msgid "Email or phone" msgstr "ایمیل یا تلفن" #: account/forms.py:144 msgid "Forgot your password?" msgstr "گذرواژه‌ات را فراموش کرده‌ای؟" #: account/forms.py:287 msgid "Email (again)" msgstr "ایمیل (دوباره)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "تاییدیه‌ی نشانی ایمیل" #: account/forms.py:302 msgid "Email (optional)" msgstr "ایمیل (اختیاری)" #: account/forms.py:314 msgid "Username (optional)" msgstr "نام‌کاربری (اختیاری)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "هربار باید ایمیل یکسانی را وارد کنید." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "گذرواژه (دوباره)" #: account/forms.py:591 msgid "Current Password" msgstr "گذرواژه کنونی" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "گذرواژه جدید" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "گذرواژه جدید (دوباره)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "کد" #: account/models.py:23 msgid "user" msgstr "کاربر" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "نشانی ایمیل" #: account/models.py:31 msgid "verified" msgstr "تاییدشده" #: account/models.py:32 msgid "primary" msgstr "اصلی" #: account/models.py:38 msgid "email addresses" msgstr "نشانی‌های ایمیل" #: account/models.py:142 msgid "created" msgstr "ایجاد‌شده" #: account/models.py:143 msgid "sent" msgstr "ارسال شد" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "کلید" #: account/models.py:149 msgid "email confirmation" msgstr "تاییدیه‌ی ایمیل" #: account/models.py:150 msgid "email confirmations" msgstr "تاییدیه‌های ایمیل" #: headless/apps.py:7 msgid "Headless" msgstr "بدون رابط کاربری" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "مشاهده شناسه کاربری شما" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "مشاهده آدرس ایمیل شما" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "مشاهده اطلاعات اولیه نمایه شما" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "اعطای مجوزها" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "استفاده از نویسه‌های عام (*) مجاز نیست مگر اینکه 'اجازه نویسه‌های عام در URI' " "فعال باشد." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' بیش از یک نویسه عام (*) دارد. فقط یک نویسه عام در هر URI مجاز است." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "نویسه‌های عام (*) فقط در بخش نام میزبان URI مجاز هستند." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "کد مجوز" #: idp/oidc/models.py:38 msgid "Device code" msgstr "کد دستگاه" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "اعتبارنامه کلاینت" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "توکن تازه‌سازی" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "محرمانه" #: idp/oidc/models.py:44 msgid "Public" msgstr "عمومی" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "محدوده‌(های) مجاز برای درخواست کلاینت. هر مقدار را در یک خط وارد کنید، مثلا: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "در صورتی که کلاینت هیچ محدوده‌ای مشخص نکند، این محدوده‌های پیش‌فرض استفاده " "می‌شوند. هر مقدار را در یک خط وارد کنید، مثلا: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "فهرست انواع مجوزهای مجاز. هر مقدار را در یک خط وارد کنید، مثلا: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "فهرست مبداهای مجاز برای درخواست‌های بین‌دامنه‌ای، هر کدام در یک خط." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "اجازه استفاده از نویسه‌های عام (*) در URIهای تغییرمسیر و مبداهای CORS. هنگام " "فعال بودن، URIها می‌توانند شامل یک ستاره برای تطبیق زیردامنه‌ها باشند." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "فهرست انواع پاسخ‌های مجاز. هر مقدار را در یک خط وارد کنید، مثلا: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "کلاینت" #: idp/oidc/models.py:116 msgid "clients" msgstr "کلاینت‌ها" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "امکان افزودن آدرس ایمیل به حسابی که توسط تایید دومرحله ای محافظت می شود وجود " "ندارد." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "امکان غیرفعالسازی تایید دومرحله ای مقدور نمی باشد." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "بدون فعال بودن احراز هویت دومرحله‌ای امکان تولید کدهای بازیابی وجود ندارد." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "تا زمانی که آدرس ایمیل خود را تایید نکرده‌اید، امکان فعال‌سازی احراز هویت " "دومرحله‌ای وجود ندارد." #: mfa/adapter.py:141 msgid "Master key" msgstr "کلید اصلی" #: mfa/adapter.py:143 msgid "Backup key" msgstr "کلید پشتیبان" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "کلید شماره {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "احراز هویت چندعاملی" #: mfa/models.py:24 msgid "Recovery codes" msgstr "کدهای بازیابی" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "احراز هویت TOTP" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "کد احراز هویت" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "بدون گذرواژه" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "فعال کردن ورود بدون گذرواژه به شما امکان می‌دهد فقط با این کلید وارد شوید، " "اما نیازمندی‌های اضافی مانند بیومتریک یا محافظت با PIN را اعمال می‌کند." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "یک حساب کاربری با این آدرس ایمیل از قبل وجود دارد. لطفا ابتدا وارد آن حساب " "شوید، سپس حساب %s خود را به آن متصل کنید." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "توکن نامعتبر است." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "هیچ گذرواژه‌ای برای حساب‌ات نهاده نشده." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "حساب‌ات هیچ رایانامه‌ي تایید‌شده‌ای ندارد." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "امکان قطع اتصال آخرین حساب شخص ثالث شما وجود ندارد." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "حساب شخص ثالث از قبل به یک حساب دیگر متصل شده است." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "حساب‌های شخص ثالث" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "فراهم‌کننده" #: socialaccount/models.py:53 msgid "provider ID" msgstr "شناسه ارائه‌دهنده" #: socialaccount/models.py:57 msgid "name" msgstr "نام" #: socialaccount/models.py:59 msgid "client id" msgstr "شناسه مشتری" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "شناسه اپ، یا کلید مصرف‌کننده" #: socialaccount/models.py:64 msgid "secret key" msgstr "کلید محرمانه" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "رمز رابک (رابط برنامه‌ی کاربردی API)، رمز مشتری، یا رمز مصرف‌کننده" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "کلید" #: socialaccount/models.py:82 msgid "social application" msgstr "اپلیکیشن اجتماعی" #: socialaccount/models.py:83 msgid "social applications" msgstr "اپلیکیشن‌های اجتماعی" #: socialaccount/models.py:118 msgid "uid" msgstr "شناسه‌کاربری" #: socialaccount/models.py:120 msgid "last login" msgstr "آخرین ورود" #: socialaccount/models.py:121 msgid "date joined" msgstr "تاریخ پیوست‌شده" #: socialaccount/models.py:122 msgid "extra data" msgstr "داده اضافی" #: socialaccount/models.py:126 msgid "social account" msgstr "حساب اجتماعی" #: socialaccount/models.py:127 msgid "social accounts" msgstr "حساب‌های اجتماعی" #: socialaccount/models.py:161 msgid "token" msgstr "توکن" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) یا توکن دسترسی (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "رمز توکن" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) یا توکن تازه‌سازی (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "انقضا" #: socialaccount/models.py:175 msgid "social application token" msgstr "توکن اپلیکشن اجتماعی" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "توکن‌های اپلیکیشن اجتماعی" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "داده نامعتبر نمایه" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "ورود" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "انصراف" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "پاسخ نامعتبر هنگام دریافت توکن درخواست از \"%s\". پاسخ: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "پاسخ نامعتبر هنگام دریافت توکن دسترسی از \"%s\"" #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "توکن درخواست‌ْای برای \"%s\" ذخیره نشده." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "توکن دسترسی‌ای برای \"%s\" ذخیره نشده." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "دسترسی به منابع خصوصی \"%s\" وجود ندارد." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "پاسخ نامعتبر هنگام دریافت توکن درخواست از \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "حساب غیرفعال" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "این حساب‌کاربری غیرفعال است." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "ما یک کد به %(recipient)s ارسال کرده‌ایم. این کد به زودی منقضی می‌شود، لطفا " "سریعا آن را وارد کنید." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "تایید" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "درخواست کد جدید" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "تایید دسترسی" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "لطفا برای حفاظت از حساب خود مجددا احراز هویت کنید." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "گزینه‌های جایگزین" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "تایید ایمیل" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "کد تایید ایمیل را وارد کنید" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "استفاده از آدرس ایمیل دیگر" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "ورود" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "کد ورود را وارد کنید" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "بازنشانی گذرواژه" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "کد بازنشانی گذرواژه را وارد کنید" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "تایید شماره تلفن" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "کد تایید تلفن را وارد کنید" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "استفاده از شماره تلفن دیگر" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "نشانی‌های رایانامه" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "نشانی‌های رایانامه زیر به حساب‌ات متصل شده‌اند:" #: templates/account/email.html:25 msgid "Verified" msgstr "تایید‌شده" #: templates/account/email.html:29 msgid "Unverified" msgstr "تایید‌نشده" #: templates/account/email.html:34 msgid "Primary" msgstr "اصلی" #: templates/account/email.html:44 msgid "Make Primary" msgstr "اصلی کردن" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "بازارسال تاییدیه" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "حذف" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "افزودن نشانی رایانامه" #: templates/account/email.html:70 msgid "Add Email" msgstr "افزودن رایانامه" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "واقعا می‌خواهی نشانی رایانامه‌ی انتخاب‌شده را حذف کنی؟" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "شما این ایمیل را دریافت کرده‌اید زیرا شما یا شخص دیگری سعی کرده‌اید با آدرس " "ایمیل زیر حساب کاربری ایجاد کنید:\n" "\n" "%(email)s\n" "\n" "اما حسابی با این آدرس ایمیل از قبل وجود دارد. اگر آن را فراموش کرده‌اید، لطفا " "از روش بازیابی گذرواژه برای دسترسی مجدد به حساب خود استفاده کنید:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "حساب کاربری از قبل وجود دارد" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "سلام از طرف %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "سپاس‌گزاریم برای استفاده از %(site_name)s.\n" "‏%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "شما این ایمیل را دریافت کرده‌اید زیرا تغییر زیر در حساب شما اعمال شده است:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "اگر این تغییر را تشخیص نمی‌دهید، لطفا فورا اقدامات امنیتی لازم را انجام دهید. " "تغییر در حساب شما از منبع زیر انجام شده است:\n" "\n" "- آدرس IP: %(ip)s\n" "- مرورگر: %(user_agent)s\n" "- تاریخ: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "ایمیل شما از %(from_email)s به %(to_email)s تغییر کرده است." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "ایمیل تغییر کرد" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "ایمیل شما تایید شده است." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "تاییدیه ایمیل" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "شما این ایمیل را دریافت کرده‌اید زیرا کاربر %(user_display)s آدرس ایمیل شما " "را برای ثبت حساب کاربری در %(site_domain)s وارد کرده است." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "کد تایید ایمیل شما در زیر آمده است. لطفا آن را در پنجره مرورگر باز خود وارد " "کنید." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "برای تایید صحت این مورد، به %(activate_url)s بروید." #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "لطفا آدرس ایمیل خود را تایید کنید" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "آدرس ایمیل %(deleted_email)s از حساب شما حذف شده است." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "ایمیل حذف شد" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "کد ورود شما در زیر آمده است. لطفا آن را در پنجره مرورگر باز خود وارد کنید." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "اگر شما این اقدام را انجام نداده‌اید، می‌توانید با خیال راحت این ایمیل را " "نادیده بگیرید." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "کد ورود" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "گذرواژه شما تغییر کرده است." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "گذرواژه تغییر کرد" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "کد بازنشانی گذرواژه شما در زیر آمده است. لطفا آن را در پنجره مرورگر باز خود " "وارد کنید." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "کد بازنشانی گذرواژه" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "شما این ایمیل را دریافت کرده‌اید زیرا شما یا شخص دیگری درخواست بازنشانی " "گذرواژه حساب کاربری شما را داده است.\n" "اگر شما این درخواست را نداده‌اید، می‌توانید با خیال راحت این ایمیل را نادیده " "بگیرید. برای بازنشانی گذرواژه، روی پیوند زیر کلیک کنید." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "در صورت فراموشی، نام‌کاربری‌ات %(username)s است." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "رایانامه‌ی بازنشانی گذرواژه" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "گذرواژه شما بازنشانی شده است." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "گذرواژه شما تنظیم شده است." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "تنظیم گذرواژه" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "شما این ایمیل را دریافت کرده‌اید زیرا شما یا شخص دیگری سعی کرده‌اید به حسابی " "با ایمیل %(email)s دسترسی پیدا کنید. اما هیچ سابقه‌ای از چنین حسابی در پایگاه " "داده ما وجود ندارد." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "اگر شما بوده‌اید، می‌توانید با استفاده از پیوند زیر یک حساب کاربری ایجاد کنید." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "حساب ناشناخته" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "آدرس ایمیل" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "ایمیل فعلی" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "در حال تغییر به" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "آدرس ایمیل شما هنوز در انتظار تایید است." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "لغو تغییر" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "تغییر به" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "تغییر ایمیل" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "تایید نشانی رایانامه" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "لطفا تایید کنید که %(email)s یک آدرس ایمیل " "برای کاربر %(user_display)s است." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "تایید %(email)s امکان‌پذیر نیست زیرا از قبل توسط حساب دیگری تایید شده است." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "این پیوند تایید ایمیل منقضی شده یا نامعتبر است. لطفا درخواست تایید ایمیل جدید ارسال کنید." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "اگر هنوز یه حساب نساختی، پس لطفا نخست %(link)sثبت‌نام%(end_link)s کن." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "ورود با کلید عبور" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "ارسال کد ورود برای من" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "خروج" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "مطمئنی می‌خواهی خارج شوی؟" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "نمی‌توانی نشانی رایانامه‌ی اصلی‌ات (%(email)s) را حذف کنی." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "رایانامه‌ی تاییدیه به %(email)s فرستاده شد." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "نشانی رایانامه‌ %(email)s را تایید کرده‌ای." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "نشانی رایانامه %(email)s حذف شد." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "با %(name)s باموفقیت وارد شدی." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "خارج شده‌ای." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "کد ورود به %(recipient)s ارسال شد." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "گذرواژه باموفقیت تغییر کرد." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "گذرواژه باموفقیت نهاده شد." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "کد تایید به %(phone)s ارسال شد." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "شما شماره تلفن %(phone)s را تایید کرده‌اید." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "نشانی رانامه اصلی نهاده شد." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "تغییر گذرواژه" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "گذرواژه‌ات را فراموش کرده‌ای؟" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "گذرواژه خود را فراموش کرده‌اید؟ آدرس ایمیل خود را در زیر وارد کنید تا ایمیلی " "برای بازنشانی آن برایتان ارسال کنیم." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "درخواست بازنشانی" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "اگر مشکلی در بازنشانی گذرواژه‌ات داری، لطفا با ما تماس بگیر." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "ما یک ایمیل برای شما ارسال کرده‌ایم. اگر آن را دریافت نکرده‌اید لطفا پوشه " "هرزنامه خود را بررسی کنید. در غیر این صورت اگر تا چند دقیقه دیگر دریافت " "نکردید با ما تماس بگیرید." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "توکن نادرست" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "پیوند بازنشانی گذرواژه نامعتبر است، احتمالا به این دلیل که قبلا استفاده شده " "است. لطفا بازنشانی گذرواژه جدید درخواست " "کنید." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "گذرواژه‌ات اکنون تغییر کرد." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "نهادن گذرواژه" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "تغییر تلفن" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "تلفن فعلی" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "شماره تلفن شما هنوز در انتظار تایید است." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "تغییر تلفن" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "گذرواژه خود را وارد کنید:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "شما یک کد ویژه برای ورود بدون گذرواژه دریافت خواهید کرد." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "درخواست کد" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "سایر روش‌های ورود" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "ثبت‌نام" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "ثبت نام" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "ازقبل یه حساب داری؟ پس لطفا %(link)sورود%(end_link)s کن." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "ثبت نام با کلید عبور" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "ثبت نام با کلید عبور" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "گزینه‌های دیگر" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "توقف ثبت‌نام" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "متاسفیم، ولی اکنون ثبت‌نام متوقف شده." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "توجه" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "شما در حال حاضر با %(user_display)s وارد شده‌اید." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "هشدار:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "در حال حاضر هیچ آدرس ایمیلی تنظیم نکرده‌اید. بهتر است یک آدرس ایمیل اضافه " "کنید تا بتوانید اعلان‌ها را دریافت کنید، گذرواژه خود را بازنشانی کنید و غیره." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "تایید نشانی رایانامه" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "ما یک ایمیل تایید برای شما ارسال کرده‌ایم. پیوند ارائه شده را دنبال کنید تا " "فرایند ثبت نام تکمیل شود. اگر ایمیل تایید را در صندوق ورودی اصلی خود " "نمی‌بینید، پوشه هرزنامه خود را بررسی کنید. اگر تا چند دقیقه دیگر ایمیل تایید " "را دریافت نکردید لطفا با ما تماس بگیرید." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "این بخش از سایت نیاز دارد که هویت شما تایید شود. برای این منظور، لازم است " "مالکیت آدرس ایمیل خود را تایید کنید. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "ما یک ایمیل تایید برای شما ارسال کرده‌ایم. لطفا روی پیوند داخل آن ایمیل کلیک " "کنید. اگر ایمیل تایید را در صندوق ورودی اصلی خود نمی‌بینید، پوشه هرزنامه خود " "را بررسی کنید. در غیر این صورت اگر تا چند دقیقه دیگر دریافت نکردید با ما " "تماس بگیرید." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "توجه: هنوز می‌توانید آدرس ایمیل " "خود را تغییر دهید." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "پیام‌ها:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "منو:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "اتصال‌های حساب" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "احراز هویت دومرحله‌ای" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "نشست‌ها" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "مجوز دادن" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s می‌خواهد به حساب %(site_name)s شما دسترسی داشته باشد." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "کد دستگاه را وارد کنید" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "کدی که روی دستگاه شما نمایش داده شده را وارد کنید." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "ادامه" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "تایید دستگاه" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "لطفا کد نمایش داده شده در %(client_name)s خود را برای مجوز دادن به این " "دستگاه تایید کنید." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "رد کردن" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "دستگاه مجاز شد" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "شما با موفقیت دستگاه %(client_name)s خود را مجاز کردید." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "دستگاه رد شد" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "مجوز دستگاه %(client_name)s شما رد شده است." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "خطا" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "وارد بمانید" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "حساب شما با احراز هویت دومرحله‌ای محافظت می‌شود. لطفا کد احراز هویت را وارد " "کنید:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "مجموعه جدیدی از کدهای بازیابی احراز هویت دومرحله‌ای تولید شده است." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "کدهای بازیابی جدید تولید شد" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "برنامه احراز هویت فعال شد." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "برنامه احراز هویت فعال شد" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "برنامه احراز هویت غیرفعال شد." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "برنامه احراز هویت غیرفعال شد" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "یک کلید امنیتی جدید اضافه شده است." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "کلید امنیتی اضافه شد" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "یک کلید امنیتی حذف شده است." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "کلید امنیتی حذف شد" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "برنامه احراز هویت" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "احراز هویت با استفاده از برنامه احراز هویت فعال است." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "برنامه احراز هویت فعال نیست." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "غیرفعال کردن" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "فعال کردن" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "کلیدهای امنیتی" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "شما %(count)s کلید امنیتی اضافه کرده‌اید." msgstr[1] "شما %(count)s کلید امنیتی اضافه کرده‌اید." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "هیچ کلید امنیتی اضافه نشده است." #: templates/mfa/index.html:62 msgid "Manage" msgstr "مدیریت" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "افزودن" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "کدهای بازیابی" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "%(unused_count)s از %(total_count)s کد بازیابی موجود است." msgstr[1] "%(unused_count)s از %(total_count)s کد بازیابی موجود است." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "هیچ کد بازیابی تنظیم نشده است." #: templates/mfa/index.html:96 msgid "View" msgstr "مشاهده" #: templates/mfa/index.html:102 msgid "Download" msgstr "دانلود" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "تولید" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "مجموعه جدیدی از کدهای بازیابی تولید شده است." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "کلید امنیتی اضافه شد." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "کلید امنیتی حذف شد." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "کد احراز هویت را وارد کنید:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "شما در حال تولید مجموعه جدیدی از کدهای بازیابی برای حساب خود هستید." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "این عمل کدهای موجود شما را باطل خواهد کرد." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "آیا مطمئن هستید؟" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "کدهای استفاده نشده" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "دانلود کدها" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "تولید کدهای جدید" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "فعال‌سازی برنامه احراز هویت" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "برای محافظت از حساب خود با احراز هویت دومرحله‌ای، کد QR زیر را با برنامه " "احراز هویت خود اسکن کنید. سپس کد تایید تولید شده توسط برنامه را در زیر وارد " "کنید." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "رمز برنامه احراز هویت" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "می‌توانید این رمز را ذخیره کنید و بعدا برای نصب مجدد برنامه احراز هویت خود از " "آن استفاده کنید." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "غیرفعال‌سازی برنامه احراز هویت" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "شما در حال غیرفعال کردن احراز هویت مبتنی بر برنامه احراز هویت هستید. آیا " "مطمئن هستید؟" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "به این مرورگر اعتماد می‌کنید؟" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "اگر به این مرورگر اعتماد کنید، دفعه بعد که وارد شوید از شما کد تایید خواسته " "نخواهد شد." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "اعتماد برای %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "اعتماد نکن" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "افزودن کلید امنیتی" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "حذف کلید امنیتی" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "آیا مطمئن هستید که می‌خواهید این کلید امنیتی را حذف کنید؟" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "استفاده" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "کلید عبور" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "کلید امنیتی" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "این کلید مشخص نمی‌کند که آیا یک کلید عبور است یا خیر." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "نامشخص" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "اضافه شده در %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "آخرین استفاده %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "ویرایش" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "ویرایش کلید امنیتی" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "ذخیره" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "ایجاد کلید عبور" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "شما در حال ایجاد یک کلید عبور برای حساب خود هستید. از آنجا که می‌توانید بعدا " "کلیدهای بیشتری اضافه کنید، می‌توانید از یک نام توصیفی برای تمایز کلیدها " "استفاده کنید." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "ایجاد" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "این قابلیت نیازمند جاوااسکریپت است." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "خطا در ورود با حساب شخص ثالث" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "هنگام تلاش برای ورود با حساب شخص ثالث شما خطایی رخ داد." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "می‌توانید با هر یک از حساب‌های شخص ثالث زیر وارد حساب خود شوید:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "در حال حاضر هیچ حساب شخص ثالثی به این حساب متصل نشده است." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "افزودن یک حساب شخص ثالث" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "یک حساب شخص ثالث از %(provider)s به حساب شما متصل شده است." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "حساب شخص ثالث متصل شد" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "یک حساب شخص ثالث از %(provider)s از حساب شما قطع شده است." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "حساب شخص ثالث قطع شد" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "اتصال %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "شما در حال اتصال یک حساب شخص ثالث جدید از %(provider)s هستید." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "ورود از طریق %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "شما در حال ورود با استفاده از یک حساب شخص ثالث از %(provider)s هستید." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "لغو ورود" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "ورودت به سایت‌مان با یکی از حساب‌هایت را لغو کردی. اگر اشتباهی شده، لطفا اقدام " "به ورود کن." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "حساب شخص ثالث متصل شد." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "حساب شخص ثالث قطع شد." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "چند قدمی ورود به %(site_name)s با حساب‌ات %(provider_name)s هستی. در گام آخر، " "لطفا فرم زیر را کامل کن:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "یا از حساب شخص ثالث استفاده کنید" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "از تمام نشست‌های دیگر خارج شدید." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "شروع شده در" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "آدرس IP" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "مرورگر" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "آخرین مشاهده در" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "فعلی" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "خروج از سایر نشست‌ها" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "نشست‌های کاربر" #: usersessions/models.py:94 msgid "session key" msgstr "کلید نشست" #, fuzzy #~ msgid "Account Connection" #~ msgstr "اتصال‌های حساب" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "گذرواژه باید حداقل {0} کاراکتر باشد." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "سلام،\n" #~ "\n" #~ "این رایانامه را دریافت کرده‌ای چون برای حساب کاربری‌ات در %(site_name)s، " #~ "از جانب خودت یا کسی دیگر، یه گذرواژه درخواست شده.\n" #~ "برای بازنشانی گذرواژه پیوند زیر را دنبال کن. وگرنه چشم‌پوشی از این هنگامی " #~ "که خودت هم درخواست نکردی می‌تواند امن باشد." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "نشانی‌های رایانامه زیر به حساب‌ات متصل شده‌اند:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "تایید نشانی رایانامه" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "لطفا با یکی از حساب‌های شخص سوم موجودت وارد شو. یا در %(site_name)sثبت‌نام کن و از زیر وارد شو:" #~ msgid "or" #~ msgstr "یا" #~ msgid "change password" #~ msgstr "تغییر گذرواژه" #~ msgid "OpenID Sign In" #~ msgstr "ورودبا OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "این نشانی رایانامه ازقبل به حساب دیگری وصل شده." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "بهت یه رایانامه فرستادیم. اگر تا چند دقیقه‌ی دیگر دریافتش نکردی باهامون " #~ "تماس بگیر." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "اطلاعات داده شده درست نیست." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "نام‌کاربری تنها می‌تواند شامل حروف، اعداد، و @/./+/-/_. باشد" #~ msgid "This username is already taken. Please choose another." #~ msgstr "این نام‌کاربری قبلا گرفته شده. لطفا یکی دیگر انتخاب کن." #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "تایید کرده‌ای که %(email)s یه نشانی " #~ "رایانامه برای کاربر %(user_display)s است." ================================================ FILE: allauth/locale/fi/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: django-allauth\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-07-25 20:25+0200\n" "Last-Translator: Juho Enala \n" "Language-Team: Finnish \n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Translated-Using: django-rosetta 0.7.6\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Tämä tili on poistettu käytöstä." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Et voi poistaa ensisijaista sähköpostiosoitettasi." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Sähköpostiosoite on jo liitetty tähän tilliin." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Annettu sähköposti tai salasana ei ole oikein." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Antamasi puhelinnumero ja/tai salasana ei ole oikein." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Tämä sähköpostiosoite on jo käytössä." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Ole hyvä ja anna nykyinen salasanasi." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Virheellinen koodi." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Virheellinen salasana." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Virheellinen tai vanhentunut avain." #: account/adapter.py:79 msgid "Invalid login." msgstr "Virheellinen kirjautuminen." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Salasanan uusimistarkiste ei kelpaa." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Et voi lisätä enempää kuin %d sähköpostiosoitetta." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Tämä puhelinnumero on jo käytössä." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "" "Liian monta virheellistä kirjautumisyritystä. Yritä myöhemmin uudelleen." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Sähköpostiosoite ei ole liitetty mihinkään käyttäjätiliin." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Puhelinnumero ei ole liitetty mihinkään käyttäjätiliin." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Ensisijaisen sähköpostiosoiteen tulee olla vahvistettu." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Käyttäjänimeä ei voi käyttää. Valitse toinen käyttäjänimi." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Annettu käyttäjänimi tai salasana ei ole oikein." #: account/adapter.py:98 msgid "Please select only one." msgstr "Valitse vain yksi." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Uuden arvon täytyy olla eri kuin nykyinen." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Ole kärsivällinen, lähetät liian monta pyyntöä." #: account/adapter.py:826 msgid "Use your password" msgstr "Käytä salasanaasi" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Käytä tunnistussovellusta tai koodia" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Käytä turva-avainta" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "Merkitty {email} vahvistetuksi." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Osoitteen {email} merkitseminen vahvistetuksi epäonnistui." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Merkitse valitut sähköpostiosoitteet vahvistetuiksi" #: account/apps.py:11 msgid "Accounts" msgstr "Tili" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "Sähköposti" #: account/fields.py:19 msgid "Email address" msgstr "Sähköpostiosoite" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Anna puhelinnumero maakoodilla (esim. +358 Suomelle)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Puhelin" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Salasanojen tulee olla samat." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Salasana" #: account/forms.py:67 msgid "Remember Me" msgstr "Muista minut" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Käyttäjänimi" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Käyttäjätunnus" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Käyttäjänimi, sähköposti tai puhelin" #: account/forms.py:117 msgid "Username or email" msgstr "Käyttäjänimi tai sähköposti" #: account/forms.py:119 msgid "Username or phone" msgstr "Käyttäjänimi tai puhelin" #: account/forms.py:121 msgid "Email or phone" msgstr "Sähköposti tai puhelin" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Unohditko salasanasi?" #: account/forms.py:287 msgid "Email (again)" msgstr "Sähköposti (uudestaan)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Sähköpostiosoitteen vahvistus" #: account/forms.py:302 msgid "Email (optional)" msgstr "Sähköpostiosoite (valinnainen)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Käyttäjänimi (valinnainen)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Sinun täytyy kirjoittaa sama sähköposti joka kerta." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Salasana (uudestaan)" #: account/forms.py:591 msgid "Current Password" msgstr "Nykyinen salasana" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Uusi salasana" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Uusi salasana (uudestaan)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Koodi" #: account/models.py:23 msgid "user" msgstr "käyttäjä" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "sähköpostiosoite" #: account/models.py:31 msgid "verified" msgstr "vahvistettu" #: account/models.py:32 msgid "primary" msgstr "ensisijainen" #: account/models.py:38 msgid "email addresses" msgstr "sähköpostiosoitteet" #: account/models.py:142 msgid "created" msgstr "luotu" #: account/models.py:143 msgid "sent" msgstr "lähetetty" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "avain" #: account/models.py:149 msgid "email confirmation" msgstr "sähköpostivarmistus" #: account/models.py:150 msgid "email confirmations" msgstr "sähköpostivarmistukset" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Näytä käyttäjätunnuksesi" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Näytä sähköpostiosoitteesi" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Näytä perusprofiilin tiedot" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Myönnä käyttöoikeudet" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Jokerimerkkejä ei sallita, ellei 'Salli URI-jokerimerkit' ole käytössä." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' sisältää useamman kuin yhden jokerimerkin (*). Vain yksi " "jokerimerkki per URI on sallittu." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Jokerimerkit ovat sallittuja vain URI:n isäntänimi-osassa." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Valtuutuskoodi" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Laitekoodi" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Asiakastunnukset" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Virkistysmerkki" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Luottamuksellinen" #: idp/oidc/models.py:44 msgid "Public" msgstr "Julkinen" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Käyttöalueet, joita asiakas voi pyytää. Anna yksi arvo per rivi, esim.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Jos asiakas ei määritä käyttöaluetta, käytetään näitä oletusarvoja. Anna " "yksi arvo per rivi, esim.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Luettelo sallituista valtuutustyypeistä. Anna yksi arvo per rivi, esim.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Luettelo sallituista alkuperistä toimialuerajat ylittäville pyynnöille, yksi " "per rivi." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Salli jokerimerkit (*) uudelleenohjaus-URI:issa ja CORS-lähteissä. Kun " "käytössä, URI:t voivat sisältää yhden tähtimerkin aliverkkotunnusten " "vastaamiseen." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Luettelo sallituista vastaustyypeistä. Anna yksi arvo per rivi, esim.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "asiakas" #: idp/oidc/models.py:116 msgid "clients" msgstr "asiakkaat" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Et voi lisätä sähköpostiosoitetta tiliin, joka on suojattu kaksivaiheisella " "tunnistuksella." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Et voi poistaa kaksivaiheista tunnistusta käytöstä." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Et voi luoda palautuskoodeja ilman, että kaksivaiheinen tunnistus on " "käytössä.Et voi luoda palautuskoodeja ilman kaksivaiheisen tunnistuksen " "käyttöönottoa." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Et voi ottaa kaksivaiheista tunnistusta käyttöön ennen kuin olet vahvistanut " "sähköpostiosoitteesi." #: mfa/adapter.py:141 msgid "Master key" msgstr "Pääavain" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Varmuuskopiointinavain" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Avain nro. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Palautuskoodit" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP-tunnistin" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Tunnistinkoodi" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Salasanaton" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Salasanattoman toiminnan käyttöönotto mahdollistaa kirjautumisen pelkällä " "avaimella, mutta vaatii lisätoimenpiteitä kuten biometrista tunnistusta tai " "PIN-koodia." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Sähköpostiosoite on jo liitetty olemassaolevaan tiliin. Kirjaudu ensin " "kyseiseen tiliin ja liitä %s-tilisi vasta sitten." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Virheellinen tunniste." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Tilillesi ei ole asetettu salasanaa." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Tiliisi ei ole liitetty vahvistettua sähköpostiosoitetta." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Et voi irrottaa viimeistä jäljellä olevaa kolmannen osapuolen tiliäsi." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Kolmannen osapuolen tili on jo liitetty toiseen tiliin." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Sosiaalisen median tilit" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "tarjoaja" #: socialaccount/models.py:53 msgid "provider ID" msgstr "tarjoajan ID" #: socialaccount/models.py:57 msgid "name" msgstr "nimi" #: socialaccount/models.py:59 msgid "client id" msgstr "asiakas id" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "Sovellus ID tai kuluttajan avain" #: socialaccount/models.py:64 msgid "secret key" msgstr "salainen avain" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API:n, asiakkaan tai kuluttajan salaisuus" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Avain" #: socialaccount/models.py:82 msgid "social application" msgstr "sosiaalinen applikaatio" #: socialaccount/models.py:83 msgid "social applications" msgstr "sosiaaliset applikaatiot" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "viimeisin sisäänkirjautuminen" #: socialaccount/models.py:121 msgid "date joined" msgstr "liittymispäivämäärä" #: socialaccount/models.py:122 msgid "extra data" msgstr "lisätiedot" #: socialaccount/models.py:126 msgid "social account" msgstr "sosiaalisen median tili" #: socialaccount/models.py:127 msgid "social accounts" msgstr "sosiaalisen median tilit" #: socialaccount/models.py:161 msgid "token" msgstr "tunniste" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) tai käyttöoikeustunniste (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "tunnisteen salaisuus" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) tai virkistysmerkki (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "vanhenee" #: socialaccount/models.py:175 msgid "social application token" msgstr "sosiaalisen sovelluksen tunniste" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "sosiaalisen sovelluksen tunnisteet" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Virheelliset profiilin tiedot" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Kirjaudu sisään" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Peruuta" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Virheellinen vastaus palvelusta \"%s\" pyyntötunnistetta haettaessa. Vastaus " "oli: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Virhe hankittaessa käyttöoikeustunnistetta palvelusta \"%s\"" #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Pyyntötunnistetta ei ole tallennettu palvelulle \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Käyttöoikeustunnistetta ei ole tallennettu palvelulle \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Ei pääsyä yksityisiin resursseihin palvelussa \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Virheellinen vastaus palvelusta \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Tili poissa käytöstä" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Tämä tili ei ole käytössä." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Lähetimme koodin osoitteeseen %(recipient)s. Koodi vanhenee pian, joten " "syötä se mahdollisimman pian." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Vahvista" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Pyydä uusi koodi" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Vahvista pääsy" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Tunnistaudu uudelleen tilisi suojaamiseksi." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Vaihtoehtoiset toiminnot" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Sähköpostin vahvistus" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Anna sähköpostin vahvistuskoodi" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Käytä eri sähköpostiosoitetta" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Kirjaudu sisään" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Anna sisäänkirjautumiskoodi" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Salasanan uusiminen" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Anna salasanan palautuskoodi" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Puhelinnumeron vahvistus" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Anna puhelinnumeron vahvistuskoodi" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Käytä eri puhelinnumeroa" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Sähköpostiosoitteet" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Seuraavat sähköpostiosoitteet on liitetty tiliisi:" #: templates/account/email.html:25 msgid "Verified" msgstr "Vahvistettu" #: templates/account/email.html:29 msgid "Unverified" msgstr "Vahvistamaton" #: templates/account/email.html:34 msgid "Primary" msgstr "Ensisijainen" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Aseta ensisijaiseksi" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Lähetä vahvistus uudelleen" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Poista" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Lisää sähköpostiosoite" #: templates/account/email.html:70 msgid "Add Email" msgstr "Lisää sähköposti" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Haluatko varmasti poistaa valitun sähköpostiosoitteen?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Saat tämän sähköpostin, koska sinä tai joku muu yritti rekisteröityä\n" "käyttäen sähköpostiosoitetta:\n" "\n" "%(email)s\n" "\n" "Tällä sähköpostiosoitteella on kuitenkin jo tili. Jos olet unohtanut tämän,\n" "käytä unohtunut salasana -toimintoa tilisi palauttamiseksi:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Tili on jo olemassa" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Terve palvelusta %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Kiitos, kun käytät %(site_name)s palvelua!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "Saat tämän sähköpostin, koska tilillesi tehtiin seuraava muutos:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Jos et tunnista tätä muutosta, ryhdy välittömästi asianmukaisiin " "turvatoimenpiteisiin. Tilisi muutos on peräisin seuraavasta:\n" "\n" "- IP-osoite: %(ip)s\n" "- Selain: %(user_agent)s\n" "- Päivämäärä: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "" "Sähköpostiosoitteesi on vaihdettu osoitteesta %(from_email)s osoitteeseen " "%(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Sähköpostiosoite vaihdettu" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Sähköpostiosoitteesi on vahvistettu." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Sähköpostin vahvistus" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Saat tämän sähköpostin, koska käyttäjä %(user_display)s on antanut " "sähköpostiosoitteesi rekisteröidäkseen tilin palvelussa %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Sähköpostin vahvistuskoodisi on listattu alla. Syötä se avoimessa " "selainikkunassasi." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "" "Vahvistaaksesi tietojen oikeellisuuden, mene osoitteeseen %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Vahvista sähköpostiosoitteesi" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "Sähköpostiosoite %(deleted_email)s on poistettu tililtäsi." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Sähköpostiosoite poistettu" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Sisäänkirjautumiskoodisi on listattu alla. Syötä se avoimessa " "selainikkunassasi." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Tämä sähköposti voidaan turvallisesti jättää huomiotta, jos et aloittanut " "tätä toimintoa." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Sisäänkirjautumiskoodi" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Salasanasi on vaihdettu." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Salasana vaihdettu" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Salasanan palautuskoodisi on listattu alla. Syötä se avoimessa " "selainikkunassasi." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Salasanan palautuskoodi" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Saat tämän sähköpostin, koska sinä tai joku muu on pyytänyt salasanan " "palautusta käyttäjätilillesi.\n" "Tämä viesti voidaan turvallisesti jättää huomiotta, jos et pyytänyt " "salasanan palautusta. Klikkaa alla olevaa linkkiä palauttaaksesi salasanasi." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Muistathan, että käyttäjätunnuksesi on %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Salasanan uusimissähköposti" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Salasanasi on palautettu." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Salasanasi on asetettu." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Salasana asetettu" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Saat tämän sähköpostin, koska sinä tai joku muu yritti käyttää tiliä " "sähköpostiosoitteella %(email)s. Meillä ei kuitenkaan ole tietoa tällaisesta " "tilistä tietokannassamme." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Jos se olit sinä, voit rekisteröityä tilille alla olevan linkin kautta." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Tuntematon tili" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Sähköpostiosoite" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Nykyinen sähköposti" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Vaihtamassa kohteeseen" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Sähköpostiosoitteesi vahvistus on vielä kesken." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Peruuta muutos" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Vaihda kohteeseen" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Vaihda sähköposti" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Vahvista sähköpostiosoite" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Vahvista, että %(email)s on käyttäjän " "%(user_display)s sähköpostiosoite." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "%(email)s-osoitetta ei voi vahvistaa, koska se on jo vahvistettu toiselle " "tilille." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Tämä sähköpostiosoitteen vahvistuslinkki on vanhentunut tai muuten " "käyttökelvoton. Voit kuitenkin pyytää uuden " "vahvistuslinkin sähköpostiosoitteellesi." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Jos et ole luonut vielä tiliä, niin %(link)srekisteröidy%(end_link)s ensin." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Kirjaudu passkey-avaimella" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Lähetä minulle sisäänkirjautumiskoodi" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Kirjaudu ulos" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Haluatko varmasti kirjautua ulos?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Et voi poistaa ensisijaista sähköpostiosoitettasi (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Vahvistusviesti on lähetetty osoitteeseen %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Sähköpostiosoite %(email)s on vahvistettu." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Poistettiin sähköpostiosoite %(email)s." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Kirjauduttiin sisään käyttäjänä %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Kirjauduit ulos." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Sisäänkirjautumiskoodi on lähetetty osoitteeseen %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Salasana vaihto onnistui." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Salasana asetettiin." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Vahvistuskoodi on lähetetty numeroon %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Vahvistit puhelinnumeron %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Ensisijainen sähköpostiosoite asetettiin." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Vaihda salasana" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Salasana unohtunut?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Unohditko salasanasi? Anna sähköpostiosoitteesi alle, niin lähetämme sinulle " "sähköpostin, jonka avulla voit palauttaa sen." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Salasanan uusiminen" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "Ota meihin yhteyttä, jos sinulla on ongelmia salasanasi uusimisessa." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Lähetimme sinulle sähköpostin. Jos et saanut sitä, tarkista " "roskapostikansiosi. Jos et silti saa sitä muutamassa minuutissa, ota meihin " "yhteyttä." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Virheellinen tunniste" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Salasanan uusimislinkki ei toiminut. Tämä voi tapahtua, jos linkki on jo " "käytetty. Voit kuitenkin uusia salasanan " "uusimisen." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Salasanasi on nyt vaihdettu." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Aseta salasana" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Vaihda puhelinnumero" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Nykyinen puhelin" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Puhelinnumerosi vahvistus on vielä kesken." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Vaihda puhelinnumero" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Anna salasanasi:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Saat erikoiskoodin salasanatonta sisäänkirjautumista varten." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Pyydä koodi" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Muut sisäänkirjautumisvaihtoehdot" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Rekisteröidy" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Rekisteröidy" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Onko sinulla jo tili? %(link)sKirjaudu sisään%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Rekisteröidy passkey-avaimella" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Passkey-rekisteröityminen" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Muut vaihtoehdot" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Rekisteröityminen on poissa käytöstä." #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Valitettavasti rekisteröityminen on pois käytöstä." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Huomio" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Olet jo kirjautunut käyttäjänä %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Varoitus:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Et ole asettanut sähköpostiosoitetta. Tämä tulisi tehdä, jotta voit saada " "ilmoituksia, uusia salasanasi jne." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Vahvista sähköpostiosoitteesi" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Lähetimme sinulle vahvistusviestin sähköpostitse. Klikkaa sähköpostissa " "olevaa linkkiä viimeistelläksesi rekisteröitymisprosessin. Jos et näe " "vahvistusviestiä saapuneet-kansiossasi, tarkista roskapostikansiosi. Ota " "meihin yhteyttä, jos et saa vahvistusviestiä muutaman minuutin sisällä." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Tämä osa palvelua vaatii, että tiedämme kuka olet. Tämän takia sinun pitää " "vahvistaa omistavasi ilmoittamasi sähköpostiosoite." #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Olemme lähettäneet sinulle sähköpostivahvistuksen. Klikkaa sähköpostissa " "olevaa linkkiä vahvistaaksesi sähköpostiosoitteesi. Jos et näe " "vahvistusviestiä pääpostilaatikossasi, tarkista roskapostikansiosi. Muussa " "tapauksessa ota meihin yhteyttä, jos et saa viestiä muutaman minuutin " "sisällä." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Huomio: voit edelleen vaihtaa " "sähköpostiosoitteesi." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Viestit:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Valikko:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Liitetyt tilit" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Kaksivaiheinen tunnistus" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Istunnot" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Valtuuta" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s haluaa käyttää %(site_name)s -tiliäsi." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Anna laitekoodi" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Anna laitteessasi näkyvä koodi." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Jatka" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Vahvista laite" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Vahvista %(client_name)s-sovelluksessasi näkyvä koodi valtuuttaaksesi tämän " "laitteen." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Hylkää" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Laite valtuutettu" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Olet onnistuneesti valtuuttanut %(client_name)s-laitteesi." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Laite hylätty" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "%(client_name)s-laitteesi valtuutus on hylätty." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Virhe" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Pysy kirjautuneena" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Tilisi on suojattu kaksivaiheisella tunnistuksella. Anna tunnistinkoodi:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "Uusi kaksivaiheisen tunnistuksen palautuskoodien sarja on luotu." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Uudet palautuskoodit luotu" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Tunnistinsovellus aktivoitu." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Tunnistinsovellus aktivoitu" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Tunnistinsovellus poistettu käytöstä." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Tunnistinsovellus poistettu käytöstä" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Uusi turva-avain on lisätty." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Turva-avain lisätty" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Turva-avain on poistettu." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Turva-avain poistettu" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Tunnistinsovellus" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Tunnistinsovelluksella tunnistautuminen on käytössä." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Tunnistinsovellus ei ole käytössä." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Poista käytöstä" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Aktivoi" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Turva-avaimet" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Olet lisännyt %(count)s turva-avaimen." msgstr[1] "Olet lisännyt %(count)s turva-avainta." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Turva-avaimia ei ole lisätty." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Hallinnoi" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Lisää" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Palautuskoodit" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "On olemassa %(unused_count)s / %(total_count)s palautuskoodista " "käytettävissä." msgstr[1] "" "On olemassa %(unused_count)s / %(total_count)s palautuskoodista " "käytettävissä." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Palautuskoodeja ei ole määritetty." #: templates/mfa/index.html:96 msgid "View" msgstr "Näytä" #: templates/mfa/index.html:102 msgid "Download" msgstr "Lataa" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Luo" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Uudet palautuskoodit on luotu." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Turva-avain lisätty." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Turva-avain poistettu." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Anna tunnistinsovelluksen koodi:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Olet aikeissa luoda uudet palautuskoodit tilillesi." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Tämä toiminto mitätöi nykyiset koodisi." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Oletko varma?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Käyttämättömät koodit" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Lataa koodit" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Luo uudet koodit" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Aktivoi tunnistinsovellus" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Suojataksesi tilisi kaksivaiheisella tunnistuksella, skannaa alla oleva QR-" "koodi tunnistinsovelluksellasi. Syötä sen jälkeen sovelluksen luoma " "vahvistuskoodi alla." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Tunnistinsovelluksen salainen avain" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Voit tallentaa tämän salaisen avaimen ja käyttää sitä tunnistinsovelluksesi " "uudelleenasennuksessa myöhemmin." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Poista tunnistinsovellus käytöstä" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Olet aikeissa poistaa tunnistinsovelluspohjaisen tunnistuksen käytöstä. " "Oletko varma?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Luotatko tähän selaimeen?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Jos päätät luottaa tähän selaimeen, sinulta ei kysytä vahvistuskoodia " "seuraavan kerran kirjautuessasi sisään." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Luota %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Älä luota" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Lisää turva-avain" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Poista turva-avain" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Haluatko varmasti poistaa tämän turva-avaimen?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Käyttö" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Pääsyavain" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Turva-avain" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Tämä avain ei ilmaise, onko se pääsyavain." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Määrittelemätön" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Lisätty %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Viimeksi käytetty %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Muokkaa" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Muokkaa turva-avainta" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Tallenna" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Luo pääsyavain" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Olet aikeissa luoda pääsyavaimen tilillesi. Koska voit lisätä muita avaimia " "myöhemmin, voit käyttää kuvaavaa nimeä erottaaksesi avaimet toisistaan." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Luo" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Tämä toiminto vaatii JavaScriptin." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Kolmannen osapuolen kirjautumisvirhe" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Tapahtui virhe yritettäessä kirjautua kolmannen osapuolen tilin kautta." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Voit kirjautua tilillesi käyttäen mitä tahansa seuraavista kolmannen " "osapuolen tileistä:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Tiliisi ei ole tällä hetkellä liitetty kolmannen osapuolen tilejä." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Lisää kolmannen osapuolen tili" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "" "Kolmannen osapuolen tili palvelusta %(provider)s on liitetty tilillesi." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Kolmannen osapuolen tili liitetty" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" "Kolmannen osapuolen tili palvelusta %(provider)s on erotettu tililtäsi." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Kolmannen osapuolen tili erotettu" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Yhdistä %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "" "Olet aikeissa yhdistää uuden kolmannen osapuolen tilin palvelusta " "%(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Kirjaudu sisään %(provider)s:n kautta" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" "Olet aikeissa kirjautua sisään käyttäen kolmannen osapuolen tiliä palvelusta " "%(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Sisäänkirjautuminen keskeytettiin" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Keskeytit sisäänkirjautumisen olemassaolevalle tilillesi. Jos tämä oli " "vahinko niin kirjaudu sisään." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Kolmannen osapuolen tili on liitetty." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Kolmannen osapuolen tili on erotettu." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Olet aikeissa käyttää %(provider_name)s-tiliäsi kirjautuaksesi palveluun\n" "%(site_name)s. Täytä vielä seuraava lomake:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Tai käytä kolmannen osapuolen palvelua" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Kirjauduttu ulos kaikista muista istunnoista." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Aloitettu" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP-osoite" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Selain" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Viimeksi nähty" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Nykyinen" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Kirjaudu ulos muista istunnoista" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Käyttäjäistunnot" #: usersessions/models.py:94 msgid "session key" msgstr "istuntoavain" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Liitetyt tilit" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Salasanan tulee olla vähintään {0} merkkiä pitkä." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Sait tämän sähköpostin, koska sinä tai joku muu on pyytänyt salasasi " #~ "uusimista palvelussa %(site_domain)s.\n" #~ "Tämän viestin voi jättää huomiotta, jos et pyytänyt salasanan uusimista. " #~ "Klikkaa alla olevaa linkkiä uusiaksesi salasanasi." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "Seuraavat sähköpostiosoitteet on liitetty tiliisi:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Vahvista sähköpostiosoite" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Kirjaudu sisään käyttäen kirjautumispalvelua tai rekisteröi %(site_name)s-tili ja kirjaudu " #~ "sisään alla olevalla lomakkeella:" #~ msgid "or" #~ msgstr "tai" #~ msgid "change password" #~ msgstr "vaihda salasanaa" #~ msgid "OpenID Sign In" #~ msgstr "OpenID kirjautuminen" #~ msgid "This email address is already associated with another account." #~ msgstr "Sähköpostiosoite on jo liitetty toiseen tiliin." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Olemme lähettäneet sinulle sähköpostia. Ota meihin yhteyttä, jos et saa " #~ "sitä muutaman minuutin sisällä." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "Kirjautumistiedot eivät ole oikein." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "" #~ "Käyttäjänimi saa sisältää vain kirjaimia, numeroita ja erikoismerkkejä " #~ "@/./+/-/_." #~ msgid "This username is already taken. Please choose another." #~ msgstr "Käyttäjänimi on käytössä. Valitse toinen käyttäjänimi." #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "Olet vahvistanut, että %(email)s on " #~ "sähköpostiosoite käyttäjälle %(user_display)s." ================================================ FILE: allauth/locale/fr/LC_MESSAGES/django.po ================================================ # DJANGO-ALLAUTH. # Copyright (C) 2016 # This file is distributed under the same license as the django-allauth package. # # Translators: # Steve Kossouho , 2016. # Gilou , 2019 # msgid "" msgstr "" "Project-Id-Version: django-allauth\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-09-09 09:01+0000\n" "Last-Translator: \"David D.\" \n" "Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.14-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Ce compte est actuellement désactivé." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Vous ne pouvez pas retirer votre adresse e-mail principale." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "L'adresse e-mail est déjà associée à votre compte." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "L’adresse e-mail ou le mot de passe sont incorrects." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "" "Le numéro de téléphone et/ou le mot de passe que vous avez spécifié ne sont " "pas corrects." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Un autre utilisateur utilise déjà cette adresse e-mail." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Merci d'indiquer votre mot de passe actuel." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Code incorrect." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Mot de passe incorrect." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Clé invalide ou expirée." #: account/adapter.py:79 msgid "Invalid login." msgstr "Identifiant invalide." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Le jeton de réinitialisation de mot de passe est invalide." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Vous ne pouvez pas ajouter plus de %d adresses e-mail." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Un autre utilisateur utilise déjà ce numéro de téléphone." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "" "Trop de tentatives de connexion échouées. Veuillez réessayer ultérieurement." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Cette adresse e-mail n'est pas associée à un compte utilisateur." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Le numéro de téléphone n'est associé à aucun compte utilisateur." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Votre adresse e-mail principale doit être vérifiée." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Ce pseudonyme ne peut pas être utilisé. Veuillez en choisir un autre." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Le pseudo et/ou le mot de passe sont incorrects." #: account/adapter.py:98 msgid "Please select only one." msgstr "Veuillez en sélectionner un seul." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "La nouvelle valeur doit être différente de l'actuelle." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Soyez patient, vous envoyez trop de requêtes." #: account/adapter.py:826 msgid "Use your password" msgstr "Utilisez votre mot de passe" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Utilisez une application d'authentification ou un code" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Utilisez une clé secrète" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} marquée comme vérifiée." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Échec à marquer {email} comme vérifiée." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Marquer les adresses e-mail sélectionnées comme vérifiées" #: account/apps.py:11 msgid "Accounts" msgstr "Comptes" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-mail" #: account/fields.py:19 msgid "Email address" msgstr "Adresse e-mail" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" "Entrez un numéro de téléphone avec l'indicatif du pays (par exemple, +33 " "pour la France)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Téléphone" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Vous devez saisir deux fois le même mot de passe." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Mot de passe" #: account/forms.py:67 msgid "Remember Me" msgstr "Se souvenir de moi" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Nom d'utilisateur" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Identifiant" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Nom d'utilisateur, email ou téléphone" #: account/forms.py:117 msgid "Username or email" msgstr "Nom d'utilisateur ou e-mail" #: account/forms.py:119 msgid "Username or phone" msgstr "Nom d'utilisateur ou téléphone" #: account/forms.py:121 msgid "Email or phone" msgstr "Email ou téléphone" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Mot de passe oublié ?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-mail (confirmation)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Confirmation d'adresse e-mail" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-mail (facultatif)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Nom d'utilisateur (facultatif)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Vous devez saisir deux fois le même e-mail." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Mot de passe (confirmation)" #: account/forms.py:591 msgid "Current Password" msgstr "Mot de passe actuel" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Nouveau mot de passe" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Nouveau mot de passe (confirmation)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Code" #: account/models.py:23 msgid "user" msgstr "utilisateur" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "adresse e-mail" #: account/models.py:31 msgid "verified" msgstr "vérifiée" #: account/models.py:32 msgid "primary" msgstr "principale" #: account/models.py:38 msgid "email addresses" msgstr "adresses e-mail" #: account/models.py:142 msgid "created" msgstr "créé" #: account/models.py:143 msgid "sent" msgstr "envoyé" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "clé" #: account/models.py:149 msgid "email confirmation" msgstr "confirmation par e-mail" #: account/models.py:150 msgid "email confirmations" msgstr "confirmations par e-mail" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Voir votre identifiant utilisateur" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Voir votre adresse e-mail" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Voir les informations basiques de votre profil" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Donner les permissions" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Les caractères génériques ne sont pas autorisés sauf si 'Autoriser les " "caractères génériques dans les URI' est activé." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "L'URI '{}' contient plus d'un caractère générique (*). Un seul caractère " "générique par URI est autorisé." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "" "Les caractères génériques ne sont autorisés que dans la partie nom d'hôte de " "l'URI." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Code d'autorisation" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Code de l'appareil" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Identifiants client" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Jeton d'actualisation" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Confidentiel" #: idp/oidc/models.py:44 msgid "Public" msgstr "Public" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Le(s) domaine(s) d’application que le client est autorisé à demander. " "Fournissez une valeur par ligne, par exemple : " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Si le client ne spécifie aucune portée, ces portées par défaut sont " "utilisées. Fournissez une valeur par ligne, par ex. : " "openid(ENTRÉE)profile(ENTRÉE)email(ENTRÉE)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Une liste des types d'autorisation autorisés. Fournissez une valeur par " "ligne, par ex. : " "authorization_code(ENTRÉE)client_credentials(ENTRÉE)refresh_token(ENTRÉE)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Une liste des origines autorisées pour les demandes d’origine croisée, une " "par ligne." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Autoriser les caractères génériques (*) dans les URI de redirection et les " "origines CORS. Lorsque cette option est activée, les URI peuvent contenir un " "seul astérisque pour correspondre aux sous-domaines." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Une liste des types de réponse autorisés. Fournissez une valeur par ligne, " "par exemple : code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "client" #: idp/oidc/models.py:116 msgid "clients" msgstr "clients" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Vous ne pouvez pas ajouter une adresse e-mail à un compte protégé par " "l'authentification à deux facteurs." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Vous ne pouvez pas désactiver l'authentification à deux facteurs." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Vous ne pouvez pas générer de codes de récupération sans avoir activé " "l'authentification à deux facteurs." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Vous ne pouvez pas activer l'authentification à deux facteurs tant que vous " "n'avez pas vérifié votre adresse e-mail." #: mfa/adapter.py:141 msgid "Master key" msgstr "Clé principale" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Clé de secours" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Clé n°{number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Codes de récupération" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "Authentificateur TOTP" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Code de l'authentificateur" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Sans mot de passe" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "L'activation des opérations sans mot de passe vous permet de vous connecter " "en utilisant seulement cette clé/appareil, mais demande des exigences plus " "élevées comme les informations biométriques ou un code PIN." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Un compte existe déjà avec cette adresse e-mail. Merci de vous connecter au " "préalable avec ce compte, et ensuite connecter votre compte %s." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Mauvais jeton d'identification." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Vous devez d'abord définir le mot de passe de votre compte." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Vous devez d'abord associer une adresse e-mail à votre compte." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Vous ne pouvez pas déconnecter votre dernier compte tiers restant." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Ce compte social est déjà connecté à un autre compte." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Comptes sociaux" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "fournisseur" #: socialaccount/models.py:53 msgid "provider ID" msgstr "ID du fournisseur" #: socialaccount/models.py:57 msgid "name" msgstr "nom" #: socialaccount/models.py:59 msgid "client id" msgstr "id client" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "ID de l'app ou clé de l'utilisateur" #: socialaccount/models.py:64 msgid "secret key" msgstr "clé secrète" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "Secret de l'API, secret du client, ou secret de l'utilisateur" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Clé" #: socialaccount/models.py:82 msgid "social application" msgstr "application sociale" #: socialaccount/models.py:83 msgid "social applications" msgstr "applications sociales" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "dernière connexion" #: socialaccount/models.py:121 msgid "date joined" msgstr "date d'inscription" #: socialaccount/models.py:122 msgid "extra data" msgstr "données supplémentaires" #: socialaccount/models.py:126 msgid "social account" msgstr "compte social" #: socialaccount/models.py:127 msgid "social accounts" msgstr "comptes sociaux" #: socialaccount/models.py:161 msgid "token" msgstr "jeton" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) ou jeton d'accès (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "jeton secret" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) ou jeton d'actualisation (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "expire le" #: socialaccount/models.py:175 msgid "social application token" msgstr "jeton de l'application sociale" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "jetons de l'application sociale" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Données de profil incorrectes" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Identifiant" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Annuler" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Réponse invalide lors de l'obtention du jeton de requête de \"%s\". La " "réponse était : %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Réponse invalide lors de l'obtention du jeton d'accès depuis \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Aucun jeton de requête sauvegardé pour \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Aucun jeton d'accès sauvegardé pour \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Impossible d'accéder aux ressources privées de \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Réponse invalide lors de l'obtention du jeton de requête de \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Compte inactif" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Ce compte est inactif." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Nous avons envoyé un code à %(recipient)s. Le code expirera bientôt, " "veuillez donc l'entrer rapidement." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Confirmer" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Demander nouveau code" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Confirmer l'accès" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Veuillez vous réauthentifier pour protéger votre compte." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Options alternatives" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Vérification par e-mail" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Saisissez le code de vérification reçu par mail" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Utiliser une adresse e-mail différente" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Connexion" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Entrez le code de connexion" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Réinitialisation du mot de passe" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Entrez le code de réinitialisation du mot de passe" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Vérification du téléphone" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Entrez le code de vérification du téléphone" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Utiliser un numéro de téléphone différent" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Adresses e-mail" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Les adresses e-mail suivantes sont associées à votre compte :" #: templates/account/email.html:25 msgid "Verified" msgstr "Vérifiée" #: templates/account/email.html:29 msgid "Unverified" msgstr "Non vérifiée" #: templates/account/email.html:34 msgid "Primary" msgstr "Principale" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Rendre principale" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Renvoyer le message de vérification" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Retirer" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Ajouter une adresse e-mail" #: templates/account/email.html:70 msgid "Add Email" msgstr "Ajouter un e-mail" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Voulez-vous vraiment retirer cette adresse e-mail ?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Vous recevez cet email car vous ou quelqu'un d'autre a demandé à créer\n" "un compte en utilisant cette adresse e-mail :\n" "\n" "%(email)s\n" "\n" "Cependant, un compte utilisant cette adresse existe déjà. Au cas où vous " "auriez\n" "oublié, merci d'utiliser la fonction de récupération de mot de passe pour\n" "récupérer votre compte :\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Ce compte existe déjà" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Bonjour, c'est %(site_name)s !" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Merci d'utiliser %(site_name)s !\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Vous recevez cet e-mail car la modification suivante a été apportée à votre " "compte :" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Si vous ne reconnaissez pas ce changement, veuillez prendre immédiatement " "les mesures de sécurité qui s'imposent. La modification de votre compte " "provient de :\n" "\n" "- L'adresse IP : %(ip)s\n" "- Navigateur : %(user_agent)s\n" "- Date : %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Votre adresse e-mail a été modifiée de %(from_email)s à %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "E-mail modifié" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Votre e-mail a été confirmé." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Confirmation par e-mail" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Vous recevez cet e-mail car l'utilisateur %(user_display)s a indiqué votre " "adresse pour se connecter à son compte sur %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Votre code de connexion est indiqué ci-dessous. Veuillez le saisir dans la " "fenêtre ouverte de votre navigateur." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Pour confirmer que cela est correct, allez sur %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Confirmez votre adresse e-mail" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "L'adresse e-mail %(deleted_email)s a été retirée de votre compte." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "E-mail retiré" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Votre code de connexion est indiqué ci-dessous. Veuillez l'entrer dans votre " "navigateur." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "Cet email peut être ignoré si vous n'avez rien effectué." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Code de connexion" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Votre mot de passe a été modifié." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Mot de passe modifié" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Votre code de réinitialisation du mot de passe est indiqué ci-dessous. " "Veuillez l'entrer dans la fenêtre de votre navigateur ouverte." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Code de réinitialisation du mot de passe" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Vous recevez cet e-mail car vous ou quelqu'un d'autre a demandé une " "réinitialisation de mot de passe pour votre compte utilisateur.\n" "Vous pouvez simplement ignorer ce message si vous n'êtes pas à l'origine de " "cette demande. Sinon, cliquez sur le lien ci-dessous pour réinitialiser " "votre mot de passe." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "" "Au cas où vous l'auriez oublié, votre nom d'utilisateur est %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "E-mail de réinitialisation de mot de passe" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Votre mot de passe a été réinitialisé." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Votre mot de passe a été défini." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Définition du mot de passe" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Vous recevez cet email car vous, ou quelqu'un d'autre, a essayé d'accéder à " "un compte avec l'email %(email)s. Cependant, nous ne disposons pas de ce " "compte dans nos bases de données." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Si c'était vous, vous pouvez vous inscrire en utilisant le lien ci-dessous." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Compte inconnu" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Adresse e-mail" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "E-mail actuel" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Changer pour" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Votre adresse e-mail est toujours en attente de vérification." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Annuler la modification" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Changer pour" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Changer d'e-mail" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Confirmer l'adresse e-mail" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Merci de confirmer que %(email)s est " "l'adresse e-mail de %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Impossible de confirmer %(email)s car il est déjà confirmé par un autre " "compte." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Ce lien de confirmation d'adresse e-mail a expiré ou n'est pas valide. " "Veuillez lancer une nouvelle demande de " "confirmation." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Si vous n'avez pas encore créé de compte, merci de vous " "%(link)senregistrer%(end_link)s au préalable." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Se connecter avec une clé d'accès" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Envoyez-moi un code de connexion" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Se déconnecter" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Êtes-vous sûr(e) de vouloir vous déconnecter ?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "" "Vous ne pouvez pas retirer votre adresse e-mail principale (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "E-mail de confirmation envoyé à %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Vous avez confirmé %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Adresse e-mail %(email)s retirée." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Connexion avec %(name)s réussie." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Vous êtes déconnecté(e)." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Un code de connexion a été envoyé à %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Mot de passe modifié avec succès." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Mot de passe défini avec succès." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Un code de vérification a été envoyé à %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Vous avez vérifié le numéro de téléphone %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Adresse e-mail principale enregistrée." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Modifier le mot de passe" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Mot de passe oublié ?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Mot de passe oublié ? Indiquez votre adresse e-mail ci-dessous et nous vous " "enverrons un e-mail pour le réinitialiser." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Réinitialiser mon mot de passe" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Merci de nous contacter si vous ne parvenez pas à réinitialiser votre mot de " "passe." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Nous vous avons envoyé un e-mail. Si vous ne l'avez pas reçu, veuillez " "vérifier votre dossier de spam. Sinon, contactez-nous si vous ne le recevez " "pas dans quelques minutes." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Mauvais jeton d'identification" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Le lien de réinitialisation du mot de passe est invalide. Il a peut être " "déjà été utilisé. Veuillez faire une nouvelle demande de réinitialisation de mot de passe." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Votre mot de passe a été modifié." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Définir un mot de passe" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Changer de numéro de téléphone" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Numéro de téléphone actuel" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Votre numéro de téléphone est toujours en attente de vérification." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Changer de numéro de téléphone" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Saisissez votre mot de passe :" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Vous recevrez un code spécial pour une connexion sans mot de passe." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Demander un code" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Autres options de connexion" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Inscription" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Création de compte" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "" "Vous avez déjà un compte ? Vous pouvez donc %(link)svous " "connecter%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "S'inscrire avec une clé d'accès" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Création de compte avec clé de sécurité" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Autres options" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Inscriptions fermées" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Nous sommes désolés, mais les inscriptions sont actuellement fermées." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Remarque" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Vous êtes déjà connecté en tant que %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Attention :" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Vous n'avez aucune adresse e-mail associée à votre compte. Vous devriez " "ajouter une adresse e-mail pour pouvoir recevoir des notifications, " "réinitialiser votre mot de passe, etc." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Vérifiez votre adresse e-mail" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Nous vous avons envoyé un e-mail pour validation. Cliquez sur le lien fourni " "dans l'e-mail pour terminer l'inscription. Si vous ne voyez pas l'e-mail de " "vérification dans votre boîte de réception, vérifiez votre dossier spam. " "Merci de nous contacter si vous ne le recevez pas d'ici quelques minutes." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Pour accéder à cette partie du site, il faut d'abord que\n" "nous ayons vérifié qui vous indiquez être. Pour cela, vous devez prouver\n" "que vous êtes bien le propriétaire de l'adresse e-mail que vous nous avez " "indiquée. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Nous vous avons envoyé un e-mail de vérification.\n" "Merci de cliquer sur le lien inclus dans ce courriel. Si vous ne voyez pas " "l'e-mail de vérification dans votre boîte de réception, vérifiez votre " "dossier spam.\n" "Contactez-nous si vous ne l'avez pas reçu d'ici quelques minutes." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Remarque : vous pouvez toujours changer votre adresse e-mail." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Messages :" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menu :" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Comptes associés" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Authentification à deux facteurs" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sessions" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Autoriser" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s veut accéder à votre compte %(site_name)s." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Entrer code d’appareil" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Entrez le code affiché sur votre appareil." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Continuer" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Confirmer l'appareil" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Veuillez confirmer le code affiché sur votre %(client_name)s pour autoriser " "cet appareil." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Renier" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Appareil autorisé" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Vous avez autorisé avec succès votre appareil %(client_name)s." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Appareil refusé" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "L’autorisation pour votre appareil %(client_name)s a été refusée." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Erreur" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Rester connecté" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Votre compte est protégé par l'authentification à deux facteurs. Veuillez " "saisir un code d'authentification :" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Un nouveau jeu de codes de récupération pour l'authentification à deux " "facteurs a été généré." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Nouveaux codes de récupération générés" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Application d'authentification activée." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Application d'authentification activée" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Application d'authentification désactivée." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Application d'authentification désactivée" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Une nouvelle clé de sécurité a été ajoutée." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Clé de sécurité ajoutée" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Une clé de sécurité a été retirée." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Clé de sécurité retirée" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Application d'authentification" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "" "L'authentification à l'aide d'une application d'authentification est active." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Aucune application d'authentification active." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Désactiver" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Activer" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Clés de sécurité" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Vous avez ajouté %(count)s clé de sécurité." msgstr[1] "Vous avez ajouté %(count)s clés de sécurité." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Aucune clé de sécurité n'a été ajoutée." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Gérer" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Ajouter" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Codes de récupération" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Il y a %(unused_count)s code de récupération disponible sur %(total_count)s." msgstr[1] "" "Il y a %(unused_count)s codes de récupération disponibles sur " "%(total_count)s." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Aucun code de récupération défini." #: templates/mfa/index.html:96 msgid "View" msgstr "Afficher" #: templates/mfa/index.html:102 msgid "Download" msgstr "Télécharger" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Générer" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Un nouveau jeu de codes de récupération a été généré." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Clé de sécurité ajoutée." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Clé de sécurité enlevée." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Saisissez un code d'authentification :" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "" "Vous êtes sur le point de générer un nouveau jeu de codes de récupération " "pour votre compte." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Cette action invalidera vos codes existants." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Êtes-vous sûr(e) ?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Codes inutilisés" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Télécharger les codes" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Générer de nouveaux codes" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Activer l'application d'authentification" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Pour protéger votre compte avec l'authentification à deux facteurs, scannez " "le code QR ci-dessous avec votre application d'authentification. Ensuite, " "saisissez le code de vérification généré par l'application ci-dessous." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Secret d'authentification" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Vous pouvez stocker ce secret et l'utiliser pour réinstaller votre " "application d'authentification ultérieurement." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Désactiver l'application d'authentification" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Vous êtes sur le point de désactiver l'authentification basée sur " "l'application d'authentification. Êtes-vous sûr(e) ?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Faire confiance à ce navigateur ?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Si vous choisissez de faire confiance à ce navigateur, aucun code de " "vérification ne vous sera demandé lors de votre prochaine connexion." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Faire confiance pendant %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Ne pas faire confiance" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Ajouter une clé de sécurité" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Enlever une clé de sécurité" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Êtes-vous sûr de vouloir supprimer cette clé de sécurité ?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Utilisation" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Clé de sécurité" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "clé de sécurité" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Cette clé n'indique pas si c'est une clé de sécurité." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Non spécifiée" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Ajoutée à %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Dernière utilisation à %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Modifier" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Modifier la clé de sécurité" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Enregistrer" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Créer une clé de sécurité" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Vous allez créer une clé de sécurité pour votre compte. Étant donne que vous " "pourrez en ajouter d'autres plus tard, vous pouvez utiliser un nom " "descriptif pour différencier les clés." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Créer" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Cette fonctionnalité nécessite JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Échec de la connexion via réseau social" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Une erreur est survenue lors de la tentative de connexion à votre compte de " "réseau social." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Vous pouvez vous connecter à votre compte en utilisant l'un des comptes " "tiers suivants :" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Aucun compte social n'est actuellement associé à ce compte." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Ajouter un compte tiers" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Un compte tiers de %(provider)s a été associé à votre compte." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Compte tiers ajouté" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "Un compte tiers de %(provider)s a été dissocié de votre compte." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Compte tiers dissocié" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Connecter %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "" "Vous êtes sur le point de connecter un nouveau compte tiers de %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Connexion via %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" "Vous êtes sur le point de vous connecter en utilisant un compte tiers de " "%(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Connexion annulée" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Vous avez annulé la connexion à notre site depuis l'un de vos comptes de " "réseau social. S'il s'agit d'une erreur, merci de vous reconnecter." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Le compte social a bien été connecté." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Le compte social a été déconnecté." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Vous êtes sur le point de vous connecter via votre compte %(provider_name)s\n" "au site %(site_name)s. Merci de compléter le formulaire suivant pour " "confirmer la connexion :" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Ou utilisez un service tiers" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Déconnecté de toutes les autres sessions." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Commencé à" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "Adresse IP" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Navigateur" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Vu pour la dernière fois à" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Actuel" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Déconnecter les autres sessions" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Sessions utilisateur" #: usersessions/models.py:94 msgid "session key" msgstr "Clé de session" #~ msgid "Account Connection" #~ msgstr "Connexion au compte" #, fuzzy #~ msgid "Use security key or device" #~ msgstr "Utilisez votre clé de sécurité ou appareil" #, fuzzy #~ msgid "Add Security Key or Device" #~ msgstr "Ajouter une clé de sécurité ou un appareil" #~ msgid "Add key or device" #~ msgstr "Ajouter une clé ou un appareil" #, fuzzy #~ msgid "Security Keys and Devices" #~ msgstr "Clés de sécurité et appareils" #, fuzzy #~ msgid "You have not added any security keys/devices." #~ msgstr "Vous n'avez pas ajouté de clés de sécurité ou d'appareils." #, fuzzy #~ msgid "Edit Security Key or Device" #~ msgstr "Modifier la clé de sécurité ou l'appareil" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Le mot de passe doit contenir au minimum {0} caractères." #, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "\"Vous recevez cet e-mail parce que vous ou quelqu'un d'autre a demandé " #~ "un mot de\n" #~ "passe pour votre compte utilisateur. Cependant, nous n'avons aucun\n" #~ "enregistrement d'un utilisateur avec l'e-mail %(email)s dans notre base " #~ "de\n" #~ "données.\n" #~ "\n" #~ "Vous pouvez ignorer en toute sécurité cet e-mail si vous n'avez pas " #~ "demandé de\n" #~ "réinitialisation de mot de passe.\n" #~ "\n" #~ "Si c'était vous, vous pouvez vous inscrire pour un compte en utilisant le " #~ "lien\n" #~ "ci-dessous." #~ msgid "The following email address is associated with your account:" #~ msgstr "Les adresses email suivantes sont associées à votre compte :" #~ msgid "Change Email Address" #~ msgstr "Changer l'adresse e-mail" #~ msgid "" #~ "To safeguard the security of your account, please enter your password:" #~ msgstr "" #~ "Pour garantir la sécurité de votre compte, veuillez entrer votre mot de " #~ "passe:" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Merci d'ouvrir une session avec l'un de vos comptes sociaux. Vous pouvez " #~ "aussi %(link)souvrir un compte%(end_link)s %(site_name)s puis vous " #~ "connecter ci-dessous :" #~ msgid "or" #~ msgstr "ou" #~ msgid "change password" #~ msgstr "modifier le mot de passe" #~ msgid "OpenID Sign In" #~ msgstr "Connexion OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "L'adresse e-mail est déjà associée à un autre compte." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Nous vous avons envoyé un e-mail. Merci de nous contacter si vous ne le " #~ "recevez pas d'ici quelques minutes." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "L'identifiant ou le mot de passe sont incorrects." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "" #~ "Un pseudonyme ne peut contenir que des lettres, des chiffres, ainsi que " #~ "@/./+/-/_." #~ msgid "This username is already taken. Please choose another." #~ msgstr "Ce pseudonyme est déjà utilisé, merci d'en choisir un autre." #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "Vous avez confirmé que l'adresse e-mail de l'utilsateur %(user_display)s " #~ "est %(email)s." #~ msgid "Thanks for using our site!" #~ msgstr "Merci d'utiliser notre site !" ================================================ FILE: allauth/locale/he/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2024-06-15 10:09+0000\n" "Last-Translator: Eyal Cherevatsky <5116133+eyalch@users.noreply.github.com>\n" "Language-Team: Hebrew \n" "Language: he\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Weblate 5.6-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "חשבון זה אינו פעיל כעת." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "לא ניתן להסיר את כתובת האימייל הראשית שלך." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "כתובת אימייל זו כבר משויכת לחשבון זה." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "כתובת האימייל ו/או הסיסמה אינם נכונים." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "מספר הטלפון ו/או הסיסמה אינם נכונים." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "משתמש אחר כבר רשום עם כתובת אימייל זו." #: account/adapter.py:75 msgid "Please type your current password." msgstr "אנא הזן את הסיסמה הנוכחית." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "קוד שגוי." #: account/adapter.py:77 msgid "Incorrect password." msgstr "סיסמה שגויה." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "מפתח פגום או פג תוקף." #: account/adapter.py:79 msgid "Invalid login." msgstr "כניסה לא חוקית." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "אסימון איפוס הסיסמה אינו תקין." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "לא ניתן להוסיף יותר מ-%d כתובות אימייל." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "משתמש אחר כבר רשום עם מספר טלפון זה." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "יותר מדי ניסיונות התחברות כושלים. אנא נסה שוב מאוחר יותר." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "כתובת האימייל אינה משויכת לאף חשבון משתמש." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "מספר הטלפון אינו משויך לאף חשבון משתמש." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "עליך לאמת את כתובת האימייל הראשית שלך." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "לא ניתן להשתמש בשם משתמש זה. אנא בחר שם משתמש אחר." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "שם המשתמש ו/או הסיסמה אינם נכונים." #: account/adapter.py:98 msgid "Please select only one." msgstr "אנא בחר/י אחד בלבד." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "הערך החדש חייב להיות שונה מהערך הנוכחי." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "אנא המתן/י, את/ה שולח/ת יותר מדי בקשות." #: account/adapter.py:826 msgid "Use your password" msgstr "להשתמש בסיסמה" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "השתמש ביישומון אימות או קוד" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "השתמש במפתח אבטחה" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} סומן כמאומת." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "סימון {email} כמאומת נכשל." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "סמן את כתובות האימייל שנבחרו כמאומתות." #: account/apps.py:11 msgid "Accounts" msgstr "חשבונות" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "אימייל" #: account/fields.py:19 msgid "Email address" msgstr "כתובת אימייל" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "הזן/י מספר טלפון כולל קידומת מדינה (למשל +972 לישראל)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "טלפון" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "יש להזין את אותה הסיסמה פעמיים." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "סיסמה" #: account/forms.py:67 msgid "Remember Me" msgstr "זכור אותי" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "שם משתמש" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "כניסה" #: account/forms.py:115 msgid "Username, email or phone" msgstr "שם משתמש, אימייל או טלפון" #: account/forms.py:117 msgid "Username or email" msgstr "שם משתמש או אימייל" #: account/forms.py:119 msgid "Username or phone" msgstr "שם משתמש או טלפון" #: account/forms.py:121 msgid "Email or phone" msgstr "אימייל או טלפון" #: account/forms.py:144 msgid "Forgot your password?" msgstr "שכחת את סיסמתך?" #: account/forms.py:287 msgid "Email (again)" msgstr "אימייל (שוב)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "אישור כתובת אימייל" #: account/forms.py:302 msgid "Email (optional)" msgstr "אימייל (לא חובה)" #: account/forms.py:314 msgid "Username (optional)" msgstr "שם משתמש (לא חובה)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "יש להזין את אותו האימייל פעמיים." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "סיסמה (שוב)" #: account/forms.py:591 msgid "Current Password" msgstr "סיסמה נוכחית" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "סיסמה חדשה" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "סיסמה חדשה (שוב)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "קוד" #: account/models.py:23 msgid "user" msgstr "משתמש" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "כתובת אימייל" #: account/models.py:31 msgid "verified" msgstr "מאומת" #: account/models.py:32 msgid "primary" msgstr "ראשי" #: account/models.py:38 msgid "email addresses" msgstr "כתובות אימייל" #: account/models.py:142 msgid "created" msgstr "נוצר" #: account/models.py:143 msgid "sent" msgstr "נשלח" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "מפתח" #: account/models.py:149 msgid "email confirmation" msgstr "אישור באימייל" #: account/models.py:150 msgid "email confirmations" msgstr "אישורים בדואל" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "צפה במזהה המשתמש שלך" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "צפה בכתובת האימייל שלך" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "צפה בפרטי הפרופיל הבסיסיים שלך" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "הענק הרשאות" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "תווי כוכבית אינם מותרים אלא אם 'אפשר תווי כוכבית ב-URI' מופעל." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "URI '{}' מכיל יותר מתו כוכבית (*) אחד. מותר רק תו כוכבית אחד לכל URI." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "תווי כוכבית מותרים רק בחלק שם המארח של ה-URI." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "קוד הרשאה" #: idp/oidc/models.py:38 msgid "Device code" msgstr "קוד מכשיר" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "פרטי זיהוי לקוח" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "אסימון רענון" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "חסוי" #: idp/oidc/models.py:44 msgid "Public" msgstr "ציבורי" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "היקף ההרשאות שהלקוח רשאי לבקש. ספק ערך אחד לכל שורה, למשל: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "במקרה שהלקוח לא מציין היקף הרשאות, היקפי ברירת המחדל הבאים ישמשו. ספק ערך " "אחד לכל שורה, למשל: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "רשימת סוגי הרשאה מותרים. ספק ערך אחד לכל שורה, למשל: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "רשימת מקורות מותרים לבקשות חוצות-מקור, אחד לכל שורה." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "אפשר תווי כוכבית (*) ב-URI של הפניות ומקורות CORS. כאשר מופעל, URIs יכולים " "להכיל כוכבית בודדת להתאמת תת-דומיינים." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "רשימת סוגי תגובה מותרים. ספק ערך אחד לכל שורה, למשל: code(ENTER)id_token " "token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "לקוח" #: idp/oidc/models.py:116 msgid "clients" msgstr "לקוחות" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "לא ניתן להוסיף כתובת אימייל לחשבון שמוגן באימות דו-שלבי." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "לא ניתן לבטל אימות דו-שלבי." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "לא ניתן ליצור קודי שחזור מבלי שאימות דו-שלבי יהיה מופעל." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "לא ניתן להפעיל אימות דו-שלבי לפני שאימתת את כתובת האימייל." #: mfa/adapter.py:141 msgid "Master key" msgstr "מפתח ראשי" #: mfa/adapter.py:143 msgid "Backup key" msgstr "מפתח גיבוי" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "מפתח מס' {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "אימות רב-שלבי" #: mfa/models.py:24 msgid "Recovery codes" msgstr "קודי שחזור" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "מאמת TOTP" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "קוד אימות" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "ללא סיסמה" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "הפעלת מצב ללא סיסמה מאפשרת לך להיכנס באמצעות מפתח זה בלבד, אך מחייבת דרישות " "נוספות כגון זיהוי ביומטרי או הגנת PIN." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "קיים כבר חשבון עם כתובת אימייל זו. אנא התחבר לחשבון זה, ואז קשר את חשבון %s " "שלך." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "אסימון פגום." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "לא נבחרה סיסמה לחשבונך." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "לא נמצאו כתובות אימייל מאומתות לחשבונך." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "לא ניתן לנתק את חשבון הצד-השלישי האחרון שנשאר." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "חשבון צד שלישי זה כבר מקושר לחשבון אחר." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "חשבונות חברתיים" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "ספק" #: socialaccount/models.py:53 msgid "provider ID" msgstr "מזהה ספק" #: socialaccount/models.py:57 msgid "name" msgstr "שם" #: socialaccount/models.py:59 msgid "client id" msgstr "מזהה לקוח" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "מזהה יישום, או מפתח צרכן" #: socialaccount/models.py:64 msgid "secret key" msgstr "מפתח סודי" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "סוד API, סוד לקוח או סוד צרכן" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "מפתח" #: socialaccount/models.py:82 msgid "social application" msgstr "יישום חברתי" #: socialaccount/models.py:83 msgid "social applications" msgstr "יישומים חברתיים" #: socialaccount/models.py:118 msgid "uid" msgstr "מזהה ייחודי" #: socialaccount/models.py:120 msgid "last login" msgstr "התחברות אחרונה" #: socialaccount/models.py:121 msgid "date joined" msgstr "תאריך הצטרפות" #: socialaccount/models.py:122 msgid "extra data" msgstr "מידע נוסף" #: socialaccount/models.py:126 msgid "social account" msgstr "חשבון חברתי" #: socialaccount/models.py:127 msgid "social accounts" msgstr "חשבונות חברתיים" #: socialaccount/models.py:161 msgid "token" msgstr "אסימון" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) או אסימון גישה (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "סוד אסימון" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) או אסימון רענון (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "פג תוקף בתאריך" #: socialaccount/models.py:175 msgid "social application token" msgstr "אסימון יישום חברתי" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "אסימוני יישום חברתי" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "מידע פרופיל שגוי" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "כניסה" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "ביטול" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "תגובה לא חוקית בעת קבלת אסימון בקשה מ-\"%s\". התגובה הייתה: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "תגובה לא חוקית בעת קבלת אסימון גישה מ-\"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "אין אסימון בקשה שמור עבור \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "אין אסימון גישה שמור עבור \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "אין גישה למשאב פרטי ב-\"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "תגובה לא חוקית בעת קבלת אסימון בקשה מ-\"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "חשבון לא פעיל" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "חשבון זה אינו פעיל." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "שלחנו קוד ל-%(recipient)s. הקוד יפוג בקרוב, אז נא להזין אותו בהקדם." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "אמת" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "בקש קוד חדש" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "אימות גישה" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "אנא הזדהה מחדש כדי להגן על חשבונך." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "אפשרויות חלופיות" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "אימות אימייל" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "הזן קוד אימות אימייל" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "השתמש בכתובת אימייל אחרת" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "כניסה" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "הזן קוד כניסה" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "איפוס סיסמה" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "הזן קוד איפוס סיסמה" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "אימות טלפון" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "הזן קוד אימות טלפון" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "השתמש במספר טלפון אחר" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "כתובות אימייל" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "כתובות האימייל הבאות משויכות לחשבונך:" #: templates/account/email.html:25 msgid "Verified" msgstr "מאומת" #: templates/account/email.html:29 msgid "Unverified" msgstr "לא מאומת" #: templates/account/email.html:34 msgid "Primary" msgstr "ראשי" #: templates/account/email.html:44 msgid "Make Primary" msgstr "הפוך לראשי" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "שלח אימייל אימות מחדש" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "הסר" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "הוסף כתובת אימייל" #: templates/account/email.html:70 msgid "Add Email" msgstr "הוסף אימייל" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "האם ברצונך להסיר את כתובות האימייל המסומנות?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "את/ה מקבל/ת מייל זה מכיוון שאת/ה או מישהו אחר ניסה להירשם\n" "לחשבון באמצעות כתובת האימייל:\n" "\n" "%(email)s\n" "\n" "עם זאת, חשבון עם כתובת אימייל זו כבר קיים. במקרה\n" "ששכחת, אנא השתמש/י בתהליך שחזור סיסמה כדי לשחזר\n" "את חשבונך:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "החשבון כבר קיים" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "שלום מ%(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "תודה שהשתמשת באתר %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "את/ה מקבל/ת אימייל עקב השינוי הבא שבוצע בחשבונך:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "אם אינך מזהה את השינוי הזה, נא לנקוט מיד באמצעי אבטחה נאותים. השינוי בחשבונך " "מקורו ב:\n" "\n" "- כתובת IP: %(ip)s\n" "- דפדפן: %(user_agent)s\n" "- תאריך: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "האימייל שלך השתנה מ-%(from_email)s ל-%(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "אימייל השתנה" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "האימייל שלך אושר." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "אישור באימייל" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "את/ה מקבל/ת מייל זה מכיוון שהמשתמש %(user_display)s השתמש בכתובת האימייל שלך " "כדי לרשום חשבון ב-%(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "קוד אימות האימייל שלך מופיע למטה. אנא הזן/י אותו בחלון הדפדפן שפתוח." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "כדי לאשר שזה נכון, עבור/י אל %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "אנא אמת את כתובת האימייל שלך" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "כתובת האימייל %(deleted_email)s הוסרה מחשבונך." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "אימייל הוסר" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "קוד הכניסה שלך מופיע למטה. אנא הזן/י אותו בחלון הדפדפן שפתוח." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "ניתן להתעלם בבטחה ממייל זה אם לא יזמת פעולה זו." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "קוד כניסה" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "סיסמתך שונתה." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "סיסמה שונתה" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "קוד איפוס הסיסמה שלך מופיע למטה. אנא הזן/י אותו בחלון הדפדפן שפתוח." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "קוד איפוס סיסמה" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "מייל זה נשלח אליך כיוון שאת/ה או מישהו אחר ביקש לאפס את הסיסמה לחשבונך.\n" "במידה ולא ביקשת איפוס סיסמה ניתן להתעלם ממייל זה ללא חשש. לחץ על הקישור מטה " "לאיפוס סיסמתך." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "במידת ושכחת, שם המשתמש שלך הוא %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "מייל איפוס סיסמה" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "סיסמתך אופסה." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "סיסמתך הוגדרה." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "הגדרת סיסמה" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "הודעה זו נשלחה אליך כי את/ה, או מישהו אחר, ניסה לגשת לחשבון עם כתובת האימייל " "%(email)s. עם זאת, אין לנו רישום של חשבון כזה במסד הנתונים שלנו." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "אם זה היית את/ה, תוכל/י להירשם לחשבון באמצעות הקישור למטה." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "חשבון לא ידוע" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "כתובת אימייל" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "כתובת אימייל נוכחית" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "משתנה ל" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "כתובת האימייל שלך עדיין ממתינה לאימות." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "בטל שינוי" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "שנה ל" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "שנה אימייל" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "אימות כתובת אימייל" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "אנא אמת ש%(email)s היא כתובת האימייל של " "המשתמש %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "לא ניתן לאשר את %(email)s מכיוון שהיא כבר אושרה על ידי חשבון אחר." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "קישור זה לאימות כתובת אימייל פג תוקף או שאינו תקין. יש להנפיק בקשה חדשה לאימות כתובת אימייל." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "אם לא נרשמת לחשבון בעבר, אנא %(link)sהרשם%(end_link)s תחילה." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "התחבר עם מפתח גישה" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "שלחו לי קוד כניסה" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "יציאה" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "האם אתה בטוח שברצונך לצאת?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "לא ניתן להסיר את כתובת האימייל הראשית שלך (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "מייל אימות נשלח ל %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "כתובת האימייל %(email)s אומתה בהצלחה." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "כתובת האימייל %(email)s הוסרה." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "מחובר בהצלחה כ %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "התנתקת מהחשבון." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "קוד כניסה נשלח ל-%(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "הסיסמה שונתה בהצלחה." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "הסיסמה נקבעה בהצלחה." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "קוד אימות נשלח ל-%(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "אימתת את מספר הטלפון %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "כתובת אימייל ראשית הוגדרה." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "החלפת סיסמה" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "שכחת סיסמה?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "שכחת את סיסמתך? הזן את כתובת האימייל שלך כאן, ונשלח לך מייל לאיפוס הסיסמה." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "אפס את הסיסמה" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "אנא צור איתנו קשר אם אתה מתקשה לאפס את הסיסמה." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "שלחנו לך מייל. אם לא קיבלת אותו, בדוק בתיקיית הספאם שלך. אחרת פנה אלינו אם " "לא תקבל אותו תוך מספר דקות." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "אסימון פגום" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "הקישור לאיפוס הסיסמה אינו תקין, כנראה מכיוון שכבר נעשה בו שימוש. לחץ כאן " "לבקשת איפוס סיסמה מחדש." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "סיסמתך שונתה." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "קביעת סיסמה" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "שנה טלפון" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "טלפון נוכחי" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "מספר הטלפון שלך עדיין ממתין לאימות." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "שנה טלפון" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "הזינו סיסמה:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "תקבל/י קוד מיוחד לכניסה ללא סיסמה." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "בקש קוד" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "אפשרויות כניסה אחרות" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "הרשמה" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "הרשמה" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "נרשמת בעבר? %(link)sכניסה למערכת%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "הירשם באמצעות מפתח גישה" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "הרשמה עם מפתח גישה" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "אפשרויות אחרות" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "ההרשמה סגורה" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "אנו מצטערים, אך ההרשמה סגורה כעת." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "הערה" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "הנך מחובר כבר כ%(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "אזהרה:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "טרם שייכת כתובת אימייל לחשבונך. מומלץ לשייך כתובת אימייל על מנת לקבל התראות, " "לאפס סיסמה וכו'." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "אשר את כתובת הדואל שלך" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "שלחנו לך אימייל לאימות. עקוב אחר הקישור שסופק כדי לסיים את תהליך ההרשמה. אם " "אינך רואה את דוא\"ל האימות בתיבת הדואר הנכנס הראשית שלך, בדוק את תיקיית " "הספאם. אנא צור איתנו קשר אם לא תקבל את הודעת האימות תוך מספר דקות." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "חלק זה באתר דורש מאיתנו לוודא כי הנך אכן מי שאתה טוען שאתה.\n" "למטרה זו, אנו מבקשים כי תאמת בעלות על כתובת האימייל שלך. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "שלחנו אליך מייל למטרת אימות.\n" "אנא לחץ/י על הקישור בתוך אימייל זה. אם אינך רואה את מייל האימות בתיבת הדואר " "הנכנס, בדוק/בדקי בתיקיית הספאם. אם לא\n" "קיבלת את המייל תוך מספר דקות, אנא צור/צרי עמנו קשר." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "הערה: עדיין ניתן לשנות את כתובת " "האימייל שלך." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "הודעות:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "תפריט:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "קישורים לחשבון" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "אימות דו-שלבי" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "הפעלות" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "אשר" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s מבקש גישה לחשבון %(site_name)s שלך." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "הזן קוד מכשיר" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "הזן/י את הקוד המוצג על המכשיר שלך." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "המשך" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "אשר מכשיר" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "אנא אשר/י את הקוד המוצג ב-%(client_name)s שלך כדי לאשר מכשיר זה." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "דחה" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "המכשיר אושר" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "אישרת בהצלחה את מכשיר ה-%(client_name)s שלך." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "המכשיר נדחה" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "ההרשאה למכשיר ה-%(client_name)s שלך נדחתה." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "שגיאה" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "הישאר מחובר" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "חשבונך מוגן באמצעות אימות דו-שלבי. אנא הזן/י קוד מאפליקציית האימות:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "סט חדש של קודי שחזור לאימות דו-שלבי נוצר." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "קודי שחזור חדשים נוצרו" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "אפליקציית האימות הופעלה." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "אפליקציית אימות הופעלה" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "אפליקציית האימות הושבתה." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "אפליקציית אימות הושבתה" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "מפתח אבטחה חדש נוסף." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "מפתח אבטחה נוסף" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "מפתח אבטחה הוסר." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "מפתח אבטחה הוסר" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "אפליקציית אימות" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "אימות באמצעות אפליקציית אימות פעיל." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "אפליקציית אימות אינה פעילה." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "השבת" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "הפעל" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "מפתחות אבטחה" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "הוספת %(count)s מפתח אבטחה." msgstr[1] "הוספת %(count)s מפתחות אבטחה." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "לא נוספו מפתחות אבטחה." #: templates/mfa/index.html:62 msgid "Manage" msgstr "נהל" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "הוסף" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "קודי שחזור" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "יש %(unused_count)s מתוך %(total_count)s קודי שחזור זמינים." msgstr[1] "יש %(unused_count)s מתוך %(total_count)s קודי שחזור זמינים." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "לא הוגדרו קודי שחזור." #: templates/mfa/index.html:96 msgid "View" msgstr "צפה" #: templates/mfa/index.html:102 msgid "Download" msgstr "הורד" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "צור" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "סט חדש של קודי שחזור נוצר." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "מפתח אבטחה נוסף." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "מפתח אבטחה הוסר." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "הזן/י קוד מאפליקציית האימות:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "אתה עומד ליצור סט חדש של קודי שחזור לחשבונך." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "פעולה זו תבטל את הקודים הקיימים שלך." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "האם אתה בטוח?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "קודים שלא נוצלו" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "הורד קודים" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "צור קודים חדשים" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "הפעל אפליקציית אימות" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "כדי להגן על חשבונך באמצעות אימות דו-שלבי, סרוק/סרקי את קוד ה-QR למטה באמצעות " "אפליקציית האימות. לאחר מכן, הזן/י את קוד האימות שנוצר על ידי האפליקציה." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "סוד אפליקציית האימות" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "ניתן לשמור סוד זה ולהשתמש בו כדי להתקין מחדש את אפליקציית האימות בזמן מאוחר " "יותר." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "השבת אפליקציית אימות" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "אתה עומד להשבית אימות באמצעות אפליקציית אימות. האם אתה בטוח?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "לסמוך על דפדפן זה?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "אם תבחר/י לסמוך על דפדפן זה, לא תתבקש/י להזין קוד אימות בפעם הבאה שתיכנס/י." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "סמוך למשך %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "אל תסמוך" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "הוסף מפתח אבטחה" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "הסר מפתח אבטחה" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "האם אתה בטוח שברצונך להסיר מפתח אבטחה זה?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "שימוש" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "מפתח גישה" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "מפתח אבטחה" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "מפתח זה אינו מציין האם הוא מפתח גישה." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "לא צוין" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "נוסף ב-%(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "שימוש אחרון %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "ערוך" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "ערוך מפתח אבטחה" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "שמור" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "צור מפתח גישה" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "אתה עומד ליצור מפתח גישה לחשבונך. מכיוון שתוכל להוסיף מפתחות נוספים מאוחר " "יותר, תוכל להשתמש בשם תיאורי כדי להבחין בין המפתחות." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "צור" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "פונקציונליות זו דורשת JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "כשל בהתחברות דרך צד שלישי" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "אירעה שגיאה במהלך ניסיון ההתחברות באמצעות חשבון צד שלישי שלך." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "ניתן להתחבר לחשבונך באמצעות כל אחד מחשבונות צד שלישי הבאים:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "כרגע לא מקושרים חשבונות צד שלישי לחשבון זה." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "הוסף חשבון צד שלישי" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "חשבון צד שלישי מ-%(provider)s חובר לחשבונך." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "חשבון צד שלישי חובר" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "חשבון צד שלישי מ-%(provider)s נותק מחשבונך." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "חשבון צד שלישי נותק" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "חבר את %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "אתה עומד לחבר חשבון צד שלישי חדש מ-%(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "התחבר דרך %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "אתה עומד להתחבר באמצעות חשבון צד שלישי מ-%(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "התחברות בוטלה" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "ביקשת לבטל את ההתחברות לאתר זה באמצעות אחד מחשבונותיך הקיימים. במידה וטעית, " "אנא המשך להתחברות." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "חשבון צד שלישי חובר בהצלחה." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "חשבון צד שלישי נותק בהצלחה." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "אתה עומד להשתמש בחשבון %(provider_name)s שלך כדי\n" "להתחבר ל%(site_name)s. לסיום, אנא מלא את הטופס הבא:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "או השתמש בצד שלישי" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "יצאת מכל ההפעלות האחרות." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "התחלה" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "כתובת IP" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "דפדפן" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "נראה לאחרונה ב" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "נוכחי" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "התנתק מהפעלות אחרות" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "הפעלות משתמש" #: usersessions/models.py:94 msgid "session key" msgstr "מפתח הפעלה" #, fuzzy #~ msgid "Account Connection" #~ msgstr "קישורים לחשבון" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "הסיסמה חייבת להיות באורך של לפחות {0} תווים." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "שלום מ%(site_name)s!\n" #~ "\n" #~ "מייל זה נשלח אליך כיוון שאתה או מישהו אחר ביקש סיסמה עבור חשבונך ב " #~ "%(site_name)s.\n" #~ "במידה ולא ביקשת איפוס סיסמה ניתן להתעלם ממייל זה ללא חשש. לחץ על הקישור " #~ "מטה לאיפוס סיסמתך." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "כתובות האימייל הבאות משויכות לחשבונך:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "אימות כתובת אימייל" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "אנא הכנס עם אחד מהחשבונות הקיימים שלך.\n" #~ "או, %(link)sהרשם%(end_link)s לחשבון %(site_name)s והתחבר:" #~ msgid "or" #~ msgstr "או" #~ msgid "change password" #~ msgstr "החלפת סיסמה" #~ msgid "OpenID Sign In" #~ msgstr "כניסה באמצעות OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "כתובת אימייל זו כבר משויכת לחשבון אחר." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "המייל נשלח. אנא צור איתנו קשר אם הוא אינו מתקבל תוך מספר דקות." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "שם המשתמש ו/או הסיסמא אינם נכונים" #~ msgid "This username is already taken. Please choose another." #~ msgstr "שם משתמש זה כבר תפוס, אנא ציין שם אחר" #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "אישרת בהצלחה כי %(email)s הנה כתובת דואר " #~ "אלקטרוני עבור המשתמש %(user_display)s." #~ msgid "Confirmation email sent to %(email)s" #~ msgstr "דואל אישור נשלח אל %(email)s" #~ msgid "Delete Password" #~ msgstr "מחיקת סיסמא" #~ msgid "" #~ "You may delete your password since you are currently logged in using " #~ "OpenID." #~ msgstr "אתה רשאי למחוק את סיסמאתך כיוון שהנך מחובר באמצעות OpenID" #~ msgid "delete my password" #~ msgstr "מחק סיסמא" #~ msgid "Password Deleted" #~ msgstr "הסיסמא נמחקה" ================================================ FILE: allauth/locale/hr/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Bojan Mihelac , 2013-05-22 # Mislav Cimperšak . 2013-07-09 # Goran Cetušić . 2015-11-27 # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2024-04-20 21:48+0200\n" "Last-Translator: \n" "Language-Team: Bojan Mihelac \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Poedit 1.5.4\n" "X-Translated-Using: django-rosetta 0.7.2\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Ovaj korisnički račun je privremeno neaktivan." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Nije moguće ukloniti vašu primarnu e-mail adresu." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "E-mail adresa je već registrirana s ovim korisničkim računom." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "E-mail adresa i/ili lozinka nisu ispravni." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Broj telefona i/ili lozinka nisu ispravni." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Već postoji korisnik registriran s ovom e-mail adresom." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Molimo unesite trenutnu lozinku." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Neispravan kôd." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Neispravna lozinka." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Neispravan ili istekao ključ." #: account/adapter.py:79 msgid "Invalid login." msgstr "Neispravna prijava." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Token za resetiranje lozinke je neispravan." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Ne možete dodati više od %d e-mail adresa." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Već postoji korisnik registriran s ovim brojem telefona." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Previše neuspjelih pokušaja prijave. Pokušajte ponovno kasnije." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "E-mail adresa nije dodijeljena niti jednom korisničkom računu." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Broj telefona nije dodijeljen niti jednom korisničkom računu." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Vaša primarna adresa more biti potvrđena." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Nije moguće koristiti upisano korisničko ime. Molimo odaberite drugo." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Korisničko ime i/ili lozinka nisu ispravni." #: account/adapter.py:98 msgid "Please select only one." msgstr "Molimo odaberite samo jedno." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Nova vrijednost mora biti različita od trenutne." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Budite strpljivi, šaljete previše zahtjeva." #: account/adapter.py:826 msgid "Use your password" msgstr "Koristite svoju lozinku" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Koristite aplikaciju za autentifikaciju ili kôd" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Koristite sigurnosni ključ" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "Adresa {email} označena kao potvrđena." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Nije uspjelo označiti {email} kao potvrđenu." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Označi odabrane e-mail adrese kao potvrđene" #: account/apps.py:11 msgid "Accounts" msgstr "Korisnički računi" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-mail" #: account/fields.py:19 msgid "Email address" msgstr "E-mail adresa" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" "Unesite broj telefona uključujući pozivni broj države (npr. +385 za " "Hrvatsku)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefon" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Potrebno je upisati istu lozinku svaki put." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Lozinka" #: account/forms.py:67 msgid "Remember Me" msgstr "Zapamti me" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Korisničko ime" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Prijava" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Korisničko ime, e-mail ili telefon" #: account/forms.py:117 msgid "Username or email" msgstr "Korisničko ime ili e-mail" #: account/forms.py:119 msgid "Username or phone" msgstr "Korisničko ime ili telefon" #: account/forms.py:121 msgid "Email or phone" msgstr "E-mail ili telefon" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Zaboravili ste lozinku?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-mail (ponovno)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Potvrda e-mail adrese" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-mail (neobavezno)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Korisničko ime (neobavezno)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Potrebno je upisati istu e-mail adresu svaki put." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Lozinka (ponovno)" #: account/forms.py:591 msgid "Current Password" msgstr "Trenutna lozinka" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Nova lozinka" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Nova lozinka (ponovno)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Kôd" #: account/models.py:23 msgid "user" msgstr "korisnik" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "e-mail adresa" #: account/models.py:31 msgid "verified" msgstr "potvrđena" #: account/models.py:32 msgid "primary" msgstr "primarna" #: account/models.py:38 msgid "email addresses" msgstr "E-mail adrese" #: account/models.py:142 msgid "created" msgstr "stvoreno" #: account/models.py:143 msgid "sent" msgstr "poslano" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "ključ" #: account/models.py:149 msgid "email confirmation" msgstr "E-mail potvrda" #: account/models.py:150 msgid "email confirmations" msgstr "E-mail potvrde" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Pregledajte svoj korisnički ID" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Pregledajte svoju e-mail adresu" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Pregledajte osnovne podatke svog profila" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Dodijelite dozvole" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Zamjenski znakovi nisu dopušteni osim ako nije omogućena opcija 'Dopusti " "zamjenske znakove u URI-ju'." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' sadrži više od jednog zamjenskog znaka (*). Dopušten je samo jedan " "zamjenski znak po URI-ju." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "" "Zamjenski znakovi su dopušteni samo u dijelu URI-ja koji označava naziv " "poslužitelja." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Autorizacijski kôd" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Kôd uređaja" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Klijentske vjerodajnice" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Token za osvježavanje" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Povjerljivo" #: idp/oidc/models.py:44 msgid "Public" msgstr "Javno" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Opseg(i) koje klijent smije zatražiti. Unesite jednu vrijednost po retku, " "npr.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "U slučaju da klijent ne navede opseg, koriste se ovi zadani opsezi. Unesite " "jednu vrijednost po retku, npr.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Popis dopuštenih tipova odobrenja. Unesite jednu vrijednost po retku, npr.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "Popis dopuštenih izvora za zahtjeve između domena, jedan po retku." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Dopusti zamjenske znakove (*) u preusmjeravajućim URI-jima i CORS izvorima. " "Kada je omogućeno, URI-ji mogu sadržavati jednu zvjezdicu za podudaranje s " "poddomenama." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Popis dopuštenih tipova odgovora. Unesite jednu vrijednost po retku, npr.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "klijent" #: idp/oidc/models.py:116 msgid "clients" msgstr "klijenti" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Ne možete dodati e-mail adresu na račun zaštićen dvofaktorskom " "autentifikacijom." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Ne možete deaktivirati dvofaktorsku autentifikaciju." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Ne možete generirati kodove za oporavak bez omogućene dvofaktorske " "autentifikacije." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Ne možete aktivirati dvofaktorsku autentifikaciju dok ne potvrdite svoju e-" "mail adresu." #: mfa/adapter.py:141 msgid "Master key" msgstr "Glavni ključ" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Rezervni ključ" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Ključ br. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Kodovi za oporavak" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP autentifikator" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Kôd autentifikatora" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Bez lozinke" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Omogućavanje rada bez lozinke omogućuje vam prijavu samo ovim ključem, ali " "nameće dodatne zahtjeve poput biometrije ili zaštite PIN-om." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Korisnički račun s ovom e-mail adresom već postoji. Molimo da se prvo " "prijavite pod tim korisničkim računom i zatim povežete svoj %s račun." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Neispravan token." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Vaš korisnički račun nema postavljenu lozinku." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Vaš korisnički račun nema potvrđenu e-mail adresu." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Ne možete odspojiti svoj zadnji preostali račun treće strane." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Račun treće strane je već povezan s drugim korisničkim računom." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Korisnički računi" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "pružatelj" #: socialaccount/models.py:53 msgid "provider ID" msgstr "ID pružatelja" #: socialaccount/models.py:57 msgid "name" msgstr "naziv" #: socialaccount/models.py:59 msgid "client id" msgstr "ID klijenta" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "ID aplikacije ili potrošački ključ" #: socialaccount/models.py:64 msgid "secret key" msgstr "tajni ključ" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API tajna, tajna klijenta ili potrošačka tajna" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Ključ" #: socialaccount/models.py:82 msgid "social application" msgstr "društvena aplikacija" #: socialaccount/models.py:83 msgid "social applications" msgstr "društvene aplikacije" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "zadnja prijava" #: socialaccount/models.py:121 msgid "date joined" msgstr "datum pridruživanja" #: socialaccount/models.py:122 msgid "extra data" msgstr "dodatni podaci" #: socialaccount/models.py:126 msgid "social account" msgstr "društveni račun" #: socialaccount/models.py:127 msgid "social accounts" msgstr "društveni računi" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) ili pristupni token (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "tajna tokena" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) ili token za osvježavanje (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "istječe u" #: socialaccount/models.py:175 msgid "social application token" msgstr "token društvene aplikacije" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "tokeni društvenih aplikacija" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Neispravni podaci profila" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Prijava" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Odustani" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Neispravan odaziv pri dohvatu tokena zahtjeva od \"%s\". Odgovor je bio: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Neispravan odaziv pri dohvatu pristupnog tokena od \\\"%s\\\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Ne postoji pohranjeni token za \\\"%s\\\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Token za pristup za \\\"%s\\\" nije pohranjen." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Nemate pristup zaštičenim sadržajima na \\\"%s\\\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Neispravan odaziv pri dohvatu tokena zahtjeva od \\\"%s\\\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Račun je neaktivan" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Ovaj korisnički račun je neaktivan." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Poslali smo kôd na %(recipient)s. Kôd uskoro istječe, stoga ga unesite što " "prije." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Potvrda" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Zatraži novi kôd" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Potvrdite pristup" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Molimo ponovno se autentificirajte radi zaštite svog računa." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Alternativne mogućnosti" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Potvrda e-maila" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Unesite kôd za potvrdu e-maila" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Koristite drugu e-mail adresu" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Prijava" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Unesite kôd za prijavu" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Resetiranje lozinke" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Unesite kôd za resetiranje lozinke" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Potvrda telefona" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Unesite kôd za potvrdu telefona" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Koristite drugi broj telefona" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "E-mail adrese" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Slijedeće e-mail adrese su povezane sa vašim korisničkim računom" #: templates/account/email.html:25 msgid "Verified" msgstr "Potvrđena" #: templates/account/email.html:29 msgid "Unverified" msgstr "Nepotvrđena" #: templates/account/email.html:34 msgid "Primary" msgstr "Primarna" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Označi kao primarnu" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Ponovno pošalji e-mail za potvrdu" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Ukloni" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Dodaj e-mail adresu" #: templates/account/email.html:70 msgid "Add Email" msgstr "Dodaj e-mail" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Da li zaista želite ukloniti e-mail adresu?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Primili ste ovaj e-mail jer ste vi ili netko drugi pokušali registrirati\n" "račun koristeći e-mail adresu:\n" "\n" "%(email)s\n" "\n" "Međutim, račun s tom e-mail adresom već postoji. U slučaju da ste\n" "to zaboravili, koristite postupak za zaboravljenu lozinku kako biste " "obnovili\n" "svoj račun:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Račun već postoji" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Pozdrav od %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Hvala što koristite %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Primili ste ovaj e-mail jer je sljedeća promjena izvršena na vašem računu:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Ako ne prepoznajete ovu promjenu, odmah poduzmite odgovarajuće sigurnosne " "mjere. Promjena na vašem računu potječe od:\n" "\n" "- IP adresa: %(ip)s\n" "- Preglednik: %(user_agent)s\n" "- Datum: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Vaš e-mail je promijenjen s %(from_email)s na %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "E-mail promijenjen" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Vaš e-mail je potvrđen." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Potvrda e-maila" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Primili ste ovaj e-mail jer je korisnik %(user_display)s upisao vašu e-mail " "adresu za registraciju računa na %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Vaš kôd za potvrdu e-maila naveden je u nastavku. Molimo unesite ga u " "otvoreni prozor preglednika." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Za potvrdu da je ovo ispravno, idite na %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Potvrdite vašu e-mail adresu" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "E-mail adresa %(deleted_email)s je uklonjena s vašeg računa." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "E-mail uklonjen" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Vaš kôd za prijavu naveden je u nastavku. Molimo unesite ga u otvoreni " "prozor preglednika." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "Ovaj e-mail možete slobodno zanemariti ako niste pokrenuli ovu radnju." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Kôd za prijavu" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Vaša lozinka je promijenjena." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Lozinka promijenjena" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Vaš kôd za resetiranje lozinke naveden je u nastavku. Molimo unesite ga u " "otvoreni prozor preglednika." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Kôd za resetiranje lozinke" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Primili ste ovaj e-mail jer ste vi ili netko drugi zatražili resetiranje " "lozinke za vaš korisnički račun.\n" "Ako niste zatražili resetiranje lozinke, slobodno zanemarite ovaj e-mail. " "Kliknite na link u nastavku za resetiranje lozinke." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Za slučaj da ste zaboravili, vaše korisničko ime je %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "E-mail za resetiranje lozinke" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Vaša lozinka je resetirana." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Vaša lozinka je postavljena." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Lozinka postavljena" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Primili ste ovaj e-mail jer ste vi ili netko drugi pokušali pristupiti " "računu s e-mail adresom %(email)s. Međutim, nemamo nikakav zapis o takvom " "računu u našoj bazi podataka." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Ako ste to bili vi, možete se registrirati za račun koristeći link u " "nastavku." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Nepoznati račun" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "E-mail adresa" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Trenutni e-mail" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Promjena u" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Vaša e-mail adresa još čeka potvrdu." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Otkaži promjenu" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Promijeni u" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Promijeni e-mail" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Potvrdite vašu e-mail adresu" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Molimo potvrdite da je %(email)s e-mail " "adresa za korisnika %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Nije moguće potvrditi %(email)s jer je već potvrđena od strane drugog računa." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Ovaj link za potvrdu e-maila je istekao ili je neispravan. Molimo zatražite novu potvrdu e-mail adrese." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Ukoliko još niste napravili korisnički račun, prvo se " "%(link)sregistrirajte%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Prijavite se pristupnim ključem" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Pošaljite mi kôd za prijavu" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Odjava" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Jeste li sigurni da se želite odjaviti?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Nije moguće ukloniti vašu primarnu e-mail adresu %(email)s " #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "E-mail s linkom za potvrdu registracije je poslan na %(email)s" #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Potvrdili ste e-mail adresu %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Uklonjena e-mail adresa %(email)s." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Uspješno ste prijavljeni kao %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Uspješno ste se odjavili." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Kôd za prijavu je poslan na %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Lozinka je uspješno promijenjena." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Lozinka je uspješno postavljena." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Verifikacijski kôd je poslan na %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Potvrdili ste broj telefona %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Primarna e-mail adresa je postavljena" #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Promjena lozinke" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Zaboravili ste lozinku?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Zaboravili ste lozinku? Unesite svoju e-mail adresu u nastavku i poslat ćemo " "vam e-mail s uputama za resetiranje." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Resetiraj moju lozinku" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "Molimo konktaktirajte nas ukoliko imate problema pri promjeni lozinke." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Poslali smo vam e-mail. Ako ga niste primili, provjerite mapu s neželjenom " "poštom. Kontaktirajte nas ako ga ne primite u roku od nekoliko minuta." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Neispravan token" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Link za poništavanje lozinke je nevažeći, vjerojatno jer je već bio " "korišten. Molimo vas ponovite zahtjev za " "resetiranje lozinke." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Lozinka je uspješno promjenjena." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Postavljanje lozinke" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Promijeni telefon" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Trenutni telefon" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Vaš broj telefona još čeka potvrdu." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Promijeni telefon" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Unesite svoju lozinku:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Primit ćete poseban kôd za prijavu bez lozinke." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Zatraži kôd" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Ostale mogućnosti prijave" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Registracija" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Registracija" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Već imate korisnički račun? %(link)sPrijavite se%(end_link)s!" #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Registrirajte se pristupnim ključem" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Registracija pristupnim ključem" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Ostale mogućnosti" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Prijave zatvorene" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Na žalost, registracija je privremeno nedostupna." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Napomena" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Već ste prijavljeni kao %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Pažnja:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Trenutno nemate postavljenu niti jednu e-mail adresu. Postavite e-mail " "adresu kako biste mogli primati obavijesti, promijeniti lozinku i slično." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Potvrdite vašu e-mail adresu" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Poslali smo vam e-mail u svrhu potvrde. Slijedite dani link za završetak " "postupka registracije. Ako ne vidite e-mail za potvrdu u glavnom sandučiću, " "provjerite mapu s neželjenom poštom. Molimo kontaktirajte nas ukoliko ne " "primite e-mail za potvrdu unutar sljedećih nekoliko minuta." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Ovaj dio web stranice zahtijeva da potvrdimo da ste\n" "onaj za koga se predstavljate. Zbog toga je nužno da\n" "potvrdite vlasništvo nad svojom e-mail adresom. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Poslali smo vam e-mail u svrhu\n" "potvrde. Molimo kliknite na link unutar tog e-maila. Ako ne vidite e-mail za " "potvrdu u glavnom sandučiću, provjerite mapu s neželjenom poštom. U " "suprotnom\n" "nas kontaktirajte ako ga ne primite unutar nekoliko minuta." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Napomena: još uvijek možete promijeniti svoju e-mail adresu." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Poruke:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Izbornik:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Povezani računi" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Dvofaktorska autentifikacija" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sesije" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Autoriziraj" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s želi pristupiti vašem %(site_name)s računu." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Unesite kôd uređaja" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Unesite kôd prikazan na vašem uređaju." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Nastavi" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Potvrdite uređaj" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Molimo potvrdite kôd prikazan na vašem %(client_name)s kako biste " "autorizirali ovaj uređaj." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Odbij" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Uređaj autoriziran" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Uspješno ste autorizirali svoj %(client_name)s uređaj." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Uređaj odbijen" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Autorizacija za vaš %(client_name)s uređaj je odbijena." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Greška" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Ostani prijavljen" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Vaš račun je zaštićen dvofaktorskom autentifikacijom. Molimo unesite kôd " "autentifikatora:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Generiran je novi skup kodova za oporavak dvofaktorske autentifikacije." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Generirani novi kodovi za oporavak" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Aplikacija za autentifikaciju aktivirana." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Aplikacija za autentifikaciju aktivirana" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Aplikacija za autentifikaciju deaktivirana." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Aplikacija za autentifikaciju deaktivirana" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Dodan je novi sigurnosni ključ." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Sigurnosni ključ dodan" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Sigurnosni ključ je uklonjen." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Sigurnosni ključ uklonjen" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Aplikacija za autentifikaciju" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Autentifikacija putem aplikacije za autentifikaciju je aktivna." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Aplikacija za autentifikaciju nije aktivna." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Deaktiviraj" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Aktiviraj" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Sigurnosni ključevi" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Dodali ste %(count)s sigurnosni ključ." msgstr[1] "Dodali ste %(count)s sigurnosna ključa." msgstr[2] "Dodali ste %(count)s sigurnosnih ključeva." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Nema dodanih sigurnosnih ključeva." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Upravljaj" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Dodaj" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Kodovi za oporavak" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "Dostupno je %(unused_count)s od %(total_count)s kodova za oporavak." msgstr[1] "Dostupna su %(unused_count)s od %(total_count)s kodova za oporavak." msgstr[2] "Dostupno je %(unused_count)s od %(total_count)s kodova za oporavak." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Nema postavljenih kodova za oporavak." #: templates/mfa/index.html:96 msgid "View" msgstr "Pregledaj" #: templates/mfa/index.html:102 msgid "Download" msgstr "Preuzmi" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Generiraj" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Generiran je novi skup kodova za oporavak." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Sigurnosni ključ dodan." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Sigurnosni ključ uklonjen." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Unesite kôd autentifikatora:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Upravo ćete generirati novi skup kodova za oporavak za svoj račun." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Ova radnja će poništiti vaše postojeće kodove." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Jeste li sigurni?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Neiskorišteni kodovi" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Preuzmi kodove" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Generiraj nove kodove" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Aktiviraj aplikaciju za autentifikaciju" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Za zaštitu svog računa dvofaktorskom autentifikacijom, skenirajte QR kôd u " "nastavku svojom aplikacijom za autentifikaciju. Zatim unesite verifikacijski " "kôd koji je generirala aplikacija." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Tajna autentifikatora" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Možete pohraniti ovu tajnu i koristiti je za ponovnu instalaciju aplikacije " "za autentifikaciju kasnije." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Deaktiviraj aplikaciju za autentifikaciju" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Upravo ćete deaktivirati autentifikaciju putem aplikacije. Jeste li sigurni?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Vjerovati ovom pregledniku?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Ako odaberete da vjerujete ovom pregledniku, nećete biti upitani za " "verifikacijski kôd pri sljedećoj prijavi." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Vjeruj %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Ne vjeruj" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Dodaj sigurnosni ključ" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Ukloni sigurnosni ključ" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Jeste li sigurni da želite ukloniti ovaj sigurnosni ključ?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Uporaba" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Pristupni ključ" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Sigurnosni ključ" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Ovaj ključ ne pokazuje je li pristupni ključ." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Neodređeno" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Dodano %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Zadnje korišteno %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Uredi" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Uredi sigurnosni ključ" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Spremi" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Stvori pristupni ključ" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Upravo ćete stvoriti pristupni ključ za svoj račun. Budući da možete dodati " "dodatne ključeve kasnije, možete koristiti opisni naziv za razlikovanje " "ključeva." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Stvori" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Ova funkcionalnost zahtijeva JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Neuspjela prijava putem treće strane" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Došlo je do greške pri pokušaju prijave putem vašeg računa treće strane." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Možete se prijaviti u svoj račun koristeći jedan od sljedećih računa treće " "strane:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "" "Trenutno nemate niti jedan račun treće strane povezan s ovim korisničkim " "računom." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Dodaj račun treće strane" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Račun treće strane od %(provider)s je povezan s vašim računom." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Račun treće strane povezan" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "Račun treće strane od %(provider)s je odspojen od vašeg računa." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Račun treće strane odspojen" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Poveži %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Upravo ćete povezati novi račun treće strane od %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Prijavite se putem %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "Upravo ćete se prijaviti koristeći račun treće strane od %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Prijava otkazana" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Odlučili ste da se ne želite prijaviti na našu stranicu koristeći jedan od " "vaših postojećih računa s društvenih mreža. Ako je to bila greška, molimo " "kliknite i prijavite se." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Račun treće strane je povezan." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Račun treće strane je odspojen." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Odabrali ste da ćete koristiti vaš %(provider_name)s račun za prijavu u " "stranicu %(site_name)s. Kao posljednji korak, molimo vas ispunite sljedeći " "obrazac:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Ili koristite račun treće strane" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Odjavljeni ste iz svih ostalih sesija." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Započeto u" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP adresa" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Preglednik" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Zadnje viđeno u" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Trenutna" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Odjavi ostale sesije" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Korisničke sesije" #: usersessions/models.py:94 msgid "session key" msgstr "ključ sesije" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Povezani računi" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Lozinka treba imati najmanje {0} znakova." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Pozdravi od %(site_name)s!\n" #~ "\n" #~ "Primili ste ovaj e-mail jer ste vi ili netko drugi zatražili lozinku za " #~ "vaš korisnički račun na %(site_domain)s.\n" #~ "Ako niste zatražili resetiranje lozinke, slobodno zanemarite ovaj e-mail. " #~ "Kliknite na sljedeći link za resetiranje lozinke." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "Slijedeće e-mail adrese su povezane sa vašim korisničkim računom" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Potvrdite vašu e-mail adresu" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Molimo prijavite se s jednim od vaših postojećih računa društvenih mreža " #~ "ili se %(link)sregistrirajte%(end_link)s za korisnički račun na " #~ "%(site_name)s i prijavite se." #~ msgid "or" #~ msgstr "ili" #~ msgid "change password" #~ msgstr "promjena lozinke" #~ msgid "OpenID Sign In" #~ msgstr "Prijava s OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "E-mail adresa je već registrirana s drugim korisničkim računom." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Poslali smo vam e-mail. Molimo kontaktirajte nas ako ga ne primite unutar " #~ "nekoliko sljedećih minuta." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "Korisničko ime i/ili zaporka nisu ispravni." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "Korisničko ime može sadržavati samo slova, brojeve i @/./+/-/_." #~ msgid "This username is already taken. Please choose another." #~ msgstr "" #~ "Korisničko ime je već zauzeto. Molimo izaberite drugo korisničko ime." #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "Potvrdili ste da je %(email)s e-mail " #~ "adresa za korisnika %(user_display)s." ================================================ FILE: allauth/locale/ht/LC_MESSAGES/django.po ================================================ # DJANGO-ALLAUTH. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the django-allauth package. # # Translators: # Jean Patrick Prenis , 2025. # msgid "" msgstr "" "Project-Id-Version: django-allauth\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-08-19 12:00+0000\n" "Last-Translator: Jean Patrick Prenis \n" "Language-Team: kreyòl ayisyen\n" "Language: ht\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Kont sa a pa aktif kounye a." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Ou pa ka retire adrès imel prensipal ou." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Adrès imel sa a deja asosye ak kont sa a." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Adrès imel la/oswa modpas ou mete a pa kòrèk." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Nimewo telefòn lan/oswa modpas ou mete a pa kòrèk." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Gen yon itilizatè ki deja anrejistre ak adrès imel sa a." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Tanpri antre modpas ou genyen kounye a." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Kòd la pa kòrèk." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Modpas pa kòrèk." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Kle a pa valab oswa li ekspire." #: account/adapter.py:79 msgid "Invalid login." msgstr "Koneksyon pa valab." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Kòd pou renityalizasyon modpas la pa t valab." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Ou pa ka ajoute plis pase %d adrès imel." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Gen yon itilizatè ki deja anrejistre ak nimewo telefòn sa a." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Twòp tantativ koneksyon ki echwe. Eseye ankò pita." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Adrès imel la pa asosye ak okenn kont itilizatè." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Nimewo telefòn nan pa asosye ak okenn kont itilizatè." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Fòk ou verifye adrès imel prensipal ou." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Non itilizatè sa a pa ka itilize. Tanpri chwazi yon lòt non." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Non itilizatè sa/oswa modpas ou mete a pa kòrèk." #: account/adapter.py:98 msgid "Please select only one." msgstr "Tanpri chwazi sèlman youn." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Nouvo valè a dwe diferan de sa ki egziste kounye a." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Pran pasyans, ou ap voye twòp demann." #: account/adapter.py:826 msgid "Use your password" msgstr "Sèvi ak modpas ou" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Sèvi ak aplikasyon oswa kòd otantifikasyon" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Sèvi ak yon kle sekirite" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "Makee {email} ou kòm verifye." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Echwe pou make {email} ou a kòm verifye." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Make adrès imel ou chwazi yo kòm verifye" #: account/apps.py:11 msgid "Accounts" msgstr "Kont yo" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "Imel" #: account/fields.py:19 msgid "Email address" msgstr "Adrès imel" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Antre yon nimewo telefòn ansanm ak kòd peyi a (egzanp: +1 pou US)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefòn" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Ou dwe antre menm modpas la chak fwa." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Modpas" #: account/forms.py:67 msgid "Remember Me" msgstr "Sonje mwen" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Non itilizatè" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Konekte" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Non itilizatè, imel oswa telefòn" #: account/forms.py:117 msgid "Username or email" msgstr "Non itilizatè oswa imel" #: account/forms.py:119 msgid "Username or phone" msgstr "Non itilizatè oswa telefòn" #: account/forms.py:121 msgid "Email or phone" msgstr "Imel oswa telefòn" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Bliye modpas ou?" #: account/forms.py:287 msgid "Email (again)" msgstr "Imel (ankò)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Konfimasyon adrès imel" #: account/forms.py:302 msgid "Email (optional)" msgstr "Imel (opsyonèl)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Non itilizatè (opsyonèl)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Ou dwe antre menm imel la chak fwa." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Modpas (ankò)" #: account/forms.py:591 msgid "Current Password" msgstr "Modpas aktyèl" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Nouvo modpas" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Nouvo modpas (ankò)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Kòd" #: account/models.py:23 msgid "user" msgstr "itilizatè" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "adrès imel" #: account/models.py:31 msgid "verified" msgstr "verifye" #: account/models.py:32 msgid "primary" msgstr "prensipal" #: account/models.py:38 msgid "email addresses" msgstr "adrès imel yo" #: account/models.py:142 msgid "created" msgstr "kreye" #: account/models.py:143 msgid "sent" msgstr "voye" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "kle" #: account/models.py:149 msgid "email confirmation" msgstr "konfimasyon imel" #: account/models.py:150 msgid "email confirmations" msgstr "konfimasyon imel yo" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Gade ID itilizatè ou" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Gade adrès imel ou" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Gade enfòmasyon debaz pwofil ou" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Bay otorizasyon" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "Karaktè wildcard yo pa otorize sof si 'Pèmèt URI wildcard' aktive." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' gen plis pase yon wildcard (*). Yon sèl wildcard pou chak URI " "otorize." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Wildcard yo otorize sèlman nan pati non lòt la nan URI a." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Kòd otorizasyon" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Kòd aparèy" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Idantifikasyon kliyan" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Token renouvle" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Konfidansyèl" #: idp/oidc/models.py:44 msgid "Public" msgstr "Piblik" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Domèn kliyan an gen dwa mande yo. Mete yon valè pa liy, egzanp: " "openid(ANTRE)profile(ANTRE)email(ANTRE)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Si kliyan an pa presize okenn domèn, sistèm nan ap itilize domèn sa yo pa " "default. Mete yon valè pa liy, egzanp: " "openid(ANTRE)profile(ANTRE)email(ANTRE)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Yon lis kalite otorizasyon ki pèmèt yo. Mete yon valè pa liy, egzanp: " "authorization_code(ANTRE)client_credentials(ANTRE)refresh_token(ANTRE)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "Yon lis sous ki otorize pou demann atravè orijin, youn pa liy." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Pèmèt wildcard (*) nan URI redireksyon ak orijin CORS. Lè li aktive, URI yo " "ka gen yon sèl asterisk pou matche ak sou-domèn." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Yon lis kalite repons ki pèmèt yo. Mete yon valè pa liy, egzanp: " "code(ANTRE)id_token token(ANTRE)" #: idp/oidc/models.py:115 msgid "client" msgstr "kliyan" #: idp/oidc/models.py:116 msgid "clients" msgstr "kliyan yo" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Ou pa ka ajoute yon adrès imel nan yon kont ki pwoteje ak otantifikasyon de " "etap." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Ou pa ka dezaktive otantifikasyon de etap." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Ou pa ka jenere kòd rekiperasyon san ou pa aktive otantifikasyon de etap." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Ou pa ka aktive otantifikasyon de etap jiskaske ou verifye adrès imel ou." #: mfa/adapter.py:141 msgid "Master key" msgstr "Kle prensipal" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Kle sovgad" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Kle nimewo {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Kòd rekiperasyon" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "Otantifikatè TOTP" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Kòd otantifikatè" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "San modpas" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Lè ou aktive koneksyon san modpas, ou ka konekte sèlman ak kle sa a, men sa " "mande kondisyon siplemantè tankou biyometrik oswa pwoteksyon PIN." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Gen yon kont ki deja egziste ak adrès imel sa a. Tanpri konekte sou kont sa " "a an premye, epi apre sa konekte kont %s ou a." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Token pa valab." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Kont ou a pa gen okenn modpas ki konfigire." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Kont ou a pa gen okenn adrès imel ki verifye." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Ou pa ka dekonekte dènye kont twazyèm pati ki rete a." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Kont twazyèm pati a deja konekte ak yon lòt kont." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Kont Sosyal yo" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "founisè" #: socialaccount/models.py:53 msgid "provider ID" msgstr "ID founisè" #: socialaccount/models.py:57 msgid "name" msgstr "non" #: socialaccount/models.py:59 msgid "client id" msgstr "id kliyan" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "ID aplikasyon oswa kle konsomatè" #: socialaccount/models.py:64 msgid "secret key" msgstr "kle sekrè" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "Sekrè API, sekrè kliyan, oswa sekrè konsomatè" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Kle" #: socialaccount/models.py:82 msgid "social application" msgstr "aplikasyon sosyal" #: socialaccount/models.py:83 msgid "social applications" msgstr "aplikasyon sosyal yo" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "dènye koneksyon" #: socialaccount/models.py:121 msgid "date joined" msgstr "dat enskripsyon" #: socialaccount/models.py:122 msgid "extra data" msgstr "done siplemantè" #: socialaccount/models.py:126 msgid "social account" msgstr "kont sosyal" #: socialaccount/models.py:127 msgid "social accounts" msgstr "kont sosyal yo" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) oswa token aksè (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "token sekrè" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) oswa token rafrechisman (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "ekspire nan" #: socialaccount/models.py:175 msgid "social application token" msgstr "token aplikasyon sosyal" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "token aplikasyon sosyal yo" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Done pwofil pa valab" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Konekte" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Anile" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Repons pa valab pandan w t ap jwenn token demann nan nan \"%s\". Repons lan " "te: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Repons pa valab pandan w t ap jwenn token aksè nan \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Pa gen okenn token demann ki sove pou \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Pa gen okenn token aksè ki sove pou \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Pa gen aksè ak resous prive nan \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Repons pa valab pandan w t ap jwenn token demann nan nan \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Kont Inaktif" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Kont sa a inaktif." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Nou voye yon kòd bay %(recipient)s. Kòd la ap ekspire byento, tanpri antre " "li rapid." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Konfime" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Mande yon nouvo kòd" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Konfime Aksè" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Tanpri reotantifye pou pwoteje kont ou." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Opsyon altènatif" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Verifikasyon Imel" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Antre Kòd Verifikasyon Imel" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Itilize yon lòt adrès imel" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Konekte" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Antre Kòd Koneksyon" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Reyajiste Modpas" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Antre Kòd Reyajisteman Modpas" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Verifikasyon Telefòn" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Antre Kòd Verifikasyon Telefòn" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Itilize yon lòt nimewo telefòn" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Adrès Imel yo" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Adrès imel sa yo asosye ak kont ou:" #: templates/account/email.html:25 msgid "Verified" msgstr "Verifye" #: templates/account/email.html:29 msgid "Unverified" msgstr "Pa verifye" #: templates/account/email.html:34 msgid "Primary" msgstr "Prensipal" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Fè Prensipal" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Re-voye Verifikasyon" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Retire" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Ajoute Adrès Imel" #: templates/account/email.html:70 msgid "Add Email" msgstr "Ajoute Imel" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Èske ou vrèman vle retire adrès imel ou chwazi a?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Ou resevwa imel sa a paske ou oswa yon lòt moun te eseye kreye yon kont ak " "adrès imel:\n" "\n" "%(email)s\n" "\n" " Men, gen deja yon kont ki egziste ak adrès imel sa a. Si ou bliye sa, " "tanpri sèvi ak pwosedi modpas bliye a pou rekipere kont ou:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Kont la Deja Egziste" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Bonjou soti nan %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Mèsi paske w ap itilize %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "Ou resevwa mesaj sa a paske gen yon chanjman ki fèt nan kont ou:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Si ou pa rekonèt chanjman sa a, tanpri pran prekosyon sekirite nesesè yo " "touswit. Chanjman nan kont ou a sòti nan:\n" "\n" "- Adrès IP: %(ip)s\n" "- Navigatè: %(user_agent)s\n" "- Dat: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Adrès imel ou te chanje soti nan %(from_email)s pou vin %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Imel Chanje" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Imel ou konfime." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Konfimasyon Imel" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Ou resevwa imel sa a paske itilizatè %(user_display)s te bay adrès imel ou " "pou anrejistre yon kont sou %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Kòd verifikasyon imel ou an ekri anba a. Tanpri antre li nan fenèt navigatè " "ki ouvè a." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Pou konfime sa, ale sou %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Tanpri Konfime Adrès Imel Ou" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "Adrès imel %(deleted_email)s te retire nan kont ou." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Imel Retire" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Kòd koneksyon ou an ekri anba a. Tanpri antre li nan fenèt navigatè ki ouvè " "a." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Ou ka inyore imel sa a san pwoblèm si se pa ou ki te inisye aksyon sa a." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Kòd Koneksyon" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Modpas ou chanje." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Modpas Chanje" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Kòd re-inisyalizasyon modpas ou an ekri anba a. Tanpri antre li nan fenèt " "navigatè ki ouvè a." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Kòd Reyinisyalize Modpas" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Ou resevwa imel sa a paske ou oswa yon lòt moun te mande yon " "reyinisyalizasyon modpas pou kont ou.\n" "Ou ka inyore mesaj sa a si ou pa t fè demann sa a. Klike sou lyen ki anba a " "pou reyinisyalize modpas ou." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Si ou bliye, non itilizatè ou se %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Imel Reyinisyalizasyon Modpas" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Yo reyinisyalize modpas ou." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Modpas ou defini." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Modpas Mete" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Ou resevwa imel sa a paske ou oswa yon lòt moun te eseye konekte nan yon " "kont ak imel %(email)s. Men, nou pa gen okenn dosye ki koresponn ak kont sa " "a nan baz done nou an." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "Si se ou menm, ou ka kreye yon kont avèk lyen ki anba a." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Kont Enkonu" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Adrès Imèl" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Imèl aktyèl" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Chanje pou" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Adrès imèl ou a toujou ap tann verifikasyon." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Anile Chanjman" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Chanje pou" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Chanje Imèl" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Konfime Adrès Imèl" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Tanpri konfime ke %(email)s se yon adrès " "imèl pou itilizatè %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "Nou pa ka konfime %(email)s paske li deja konfime pa yon lòt kont." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Lyen konfimasyon imèl sa a ekspire oswa li pa valab. Tanpri fè yon nouvo demann konfimasyon imèl." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Si ou poko kreye yon kont, tanpri %(link)senskri%(end_link)s an premye." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Konekte ak yon kle pas" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Voye yon kòd koneksyon ban mwen" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Dekonekte" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Èske ou sèten ou vle dekonekte?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Ou pa ka retire adrès imèl prensipal ou a (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Yon imèl konfimasyon te voye bay %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Ou konfime %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Adrès imèl %(email)s te retire." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Ou konekte avèk siksè kòm %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Ou dekonekte." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Yon kòd koneksyon te voye bay %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Modpas chanje avèk siksè." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Modpas mete avèk siksè." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Yon kòd verifikasyon te voye bay %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Ou verifye nimewo telefòn %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Adrès imel prensipal la mete." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Chanje modpas" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Ou bliye modpas ou?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Ou bliye modpas ou? Mete adrès imel ou anba a, n ap voye yon imel pou ou pou " "w ka rekipere l." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Reyajiste modpas mwen" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "Tanpri kontakte nou si w rankontre pwoblèm pou reyajiste modpas ou." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Nou voye yon imel pou ou. Si ou poko resevwa li, tanpri gade nan dosye spam " "ou. Sinon kontakte nou si ou poko jwenn li apre kèk minit." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Move kòd" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Lyèn pou reyajiste modpas la pa valab, petèt paske li te deja itilize. " "Tanpri mande yon nouvo reyajistreman " "modpas." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Modpas ou chanje kounye a." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Mete modpas" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Chanje nimewo" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Nimewo aktyèl" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Nimewo telefòn ou toujou ap tann verifikasyon." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Chanje nimewo" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Antre modpas ou:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Ou pral resevwa yon kòd espesyal pou konekte san modpas." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Mande kòd" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Lòt opsyon pou konekte" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Enskri" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Enskri" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Ou deja gen yon kont? Tanpri %(link)skonekte%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Enskri ak yon kle dijital" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Enskripsyon ak kle dijital" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Lòt opsyon" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Enskripsyon Fèmen" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Nou regrèt, men enskripsyon yo fèmen kounye a." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Nòt" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Ou deja konekte kòm %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Avètisman :" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Ou poko mete okenn adrès imel. Ou ta dwe ajoute yon adrès imel pou resevwa " "notifikasyon, rekipere modpas ou, elatriye." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Verifye Adrès Imèl Ou" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Nou voye yon imel pou verifye. Swiv lyen ki ladan pou finalize enskripsyon " "an. Si ou pa jwenn imel la nan bwat prensipal ou, gade nan dosye spam la. " "Tanpri kontakte nou si ou pa jwenn imel la nan kèk minit." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Pati sa a nan sit la mande pou nou verifye ke ou se moun ou di ou ye a. Pou " "rezon sa a, nou mande pou ou verifye ke adrès imel ou se pou ou." #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Nou voye yon imel ba ou pou verifye. Tanpri klike sou lyen ki ladan imel sa " "a. Si ou pa wè li nan bwat prensipal ou, gade nan dosye spam la. Sinon, " "kontakte nou si ou pa jwenn li nan kèk minit." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Nòt : ou toujou ka chanje adrès " "imel ou." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Mesaj :" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Meni :" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Koneksyon Kont" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Otentifikasyon De Faktè" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sesyon" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Otorize" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s vle jwenn aksè nan kont %(site_name)s ou." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Antre Kòd Aparèy la" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Antre kòd ki parèt sou aparèy ou a." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Kontinye" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Konfime Aparèy" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Tanpri konfime kòd ki montre sou %(client_name)s ou a pou otorize aparèy sa " "a." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Refize" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Aparèy Ototorize" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Ou fin otorize aparèy %(client_name)s ou a avèk siksè." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Aparèy Refize" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Otorizasyon pou aparèy %(client_name)s ou a te refize." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Erè" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Rete konekte" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Kont ou pwoteje ak otantifikasyon de-faktè. Tanpri antre yon kòd " "otantifikatè:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Yo fin kreye yon nouvo seri kòd rekiperasyon pou Otantifikasyon De-Faktè." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Nouvo Kòd Rekiperasyon Kreye" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Aplikasyon otantifikatè aktive." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Aplikasyon Otantifikatè Aktive" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Aplikasyon otantifikatè dezaktive." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Aplikasyon Otantifikatè Dezaktive" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Yo te ajoute yon nouvo kle sekirite." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Kle Sekirite Ajoute" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Yo te retire yon kle sekirite." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Kle Sekirite Retire" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Aplikasyon Otantifikatè" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Otantifikasyon ak yon aplikasyon otantifikatè aktive." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Pa gen aplikasyon otantifikatè ki aktive." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Dezaktive" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Aktive" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Kle Sekirite" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Ou te ajoute %(count)s kle sekirite." msgstr[1] "Ou te ajoute %(count)s kle sekirite." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Pa gen okenn kle sekirite ki te ajoute." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Jere" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Ajoute" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Kòd Rekiperasyon" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Gen %(unused_count)s sou %(total_count)s kòd rekiperasyon ki disponib." msgstr[1] "" "Gen %(unused_count)s sou %(total_count)s kòd rekiperasyon ki disponib." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Pa gen kòd rekiperasyon ki mete." #: templates/mfa/index.html:96 msgid "View" msgstr "Gade" #: templates/mfa/index.html:102 msgid "Download" msgstr "Telechaje" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Jenere" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Yo te jenere yon nouvo seri kòd rekiperasyon." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Kle sekirite ajoute." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Kle sekirite retire." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Antre yon kòd otantifikatè:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Ou pral jenere yon nouvo seri kòd rekiperasyon pou kont ou." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Aksyon sa a ap fè kòd ou genyen yo vin pa valab." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Èske ou sèten?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Kòd ki poko itilize" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Telechaje kòd yo" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Jenere nouvo kòd" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Aktive Aplikasyon Otantifikatè" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Pou pwoteje kont ou ak otantifikasyon de-faktè, eskane kòd QR ki anba a ak " "aplikasyon otantifikatè ou. Apre sa, antre kòd verifikasyon aplikasyon an " "jenere a anba a." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Secrè otantifikatè" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Ou ka estoke sekrè sa a epi sèvi avè l pou re-enstale aplikasyon " "otantifikatè ou yon lòt lè." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Dezaktive Aplikasyon Otantifikatè" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Ou pral dezaktive otantifikasyon ki baze sou aplikasyon otantifikatè. Èske " "ou sèten?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Fè konfyans navigatè sa a?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Si ou chwazi fè konfyans navigatè sa a, yo pap mande ou yon kòd verifikasyon " "pwochen fwa ou konekte." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Fè konfyans pandan %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Pa Fè Konfyans" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Ajoute Kle Sekirite" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Retire Kle Sekirite" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Èske ou sèten ou vle retire kle sekirite sa a?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Itilizasyon" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Kle Pase" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Kle Sekirite" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Kle sa a pa endike si li se yon kle pase." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Pa Espesifye" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Ajoute nan %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Dènye itilizasyon %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Modifye" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Modifye Kle Sekirite" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Sove" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Kreye Kle Pase" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Ou pral kreye yon kle pase pou kont ou. Paske ou ka ajoute lòt kle pita, ou " "ka itilize yon non ki dekri yo pou distenge kle yo youn ak lòt." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Kreye" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Fonksyonalite sa a mande JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Echèk Koneksyon Twazyèm Pati" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Gen yon erè ki rive pandan wap eseye konekte ak kont twazyèm pati ou a." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Ou ka konekte nan kont ou a itilize nenpòt nan kont twazyèm pati ki anba yo:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Kounye a, ou pa gen okenn kont twazyèm pati konekte ak kont sa a." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Ajoute yon Kont Twazyèm Pati" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Yon kont twazyèm pati soti nan %(provider)s konekte ak kont ou a." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Kont Twazyèm Pati Konekte" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" "Yon kont twazyèm pati soti nan %(provider)s dekonèkte soti nan kont ou a." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Kont Twazyèm Pati Dekonèkte" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Konekte %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Ou pral konekte yon nouvo kont twazyèm pati soti nan %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Konekte atravè %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "Ou pral konekte itilize yon kont twazyèm pati soti nan %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Koneksyon Anile" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Ou deside anile koneksyon nan sit nou an itilize youn nan kont ou deja " "genyen yo. Si sa te yon erè, tanpri kontinye pou konekte." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Kont twazyèm pati a konekte." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Kont twazyèm pati a dekonèkte." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Ou pral itilize kont %(provider_name)s ou a pou konekte nan\n" "%(site_name)s. Kòm dènye etap, tanpri ranpli fòm sa a:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Oswa itilize yon twazyèm pati" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Dekonekte nan tout lòt sesyon yo." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Kòmanse Lè" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "Adrès IP" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Navigatè" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Dènye fwa wè nan" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Kouran" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Dekonekte Lòt Sesyon" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Sesyon Itilizatè" #: usersessions/models.py:94 msgid "session key" msgstr "kle sesyon" ================================================ FILE: allauth/locale/hu/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2024-04-20 21:45+0200\n" "Last-Translator: Tamás Makó \n" "Language-Team: \n" "Language: hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 1.7.6\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "A felhasználó jelenleg nem aktív." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Az elsődleges email címed nem törölhető." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Ez az email cím már hozzá van rendelve ehhez a felhasználóhoz." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "A megadott email vagy a jelszó hibás." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Hibás az általad megadott telefonszám vagy jelszó." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Egy felhasználó már regisztrált ezzel az email címmel." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Kérlek add meg az aktuális jelszavadat!" #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Hibás kód." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Hibás jelszó" #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Érvénytelen vagy lejárt kulcs." #: account/adapter.py:79 msgid "Invalid login." msgstr "Érvénytelen belépési kísérlet." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "A jelszó visszaállító token érvénytelen." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Nem adhatsz hozzá több mint %d email címet." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Ezzel a telefonszámmal már van regisztrált felhasználó." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Túl sok sikertelen belépési kísérlet. Próbáld újra később." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Az email cím nincs hozzárendelve egyetlen felhasználóhoz sem" #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "A telefonszám cím nincs hozzárendelve egyetlen felhasználóhoz sem" #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Az elsődleges email címet ellenőriznunk kell." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Ez a felhasználói azonosító nem használható. Kérlek válassz másikat!" #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "A megadott felhasználó vagy a jelszó hibás." #: account/adapter.py:98 msgid "Please select only one." msgstr "Kérlek csak egyet válassz!" #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Az új értéknek különböznie kell a jelenlegitől." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Légy türelemmel, túl sok kérést küldtél." #: account/adapter.py:826 msgid "Use your password" msgstr "Használd a jelszavadat" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Használj hitelesítő alkalmazást vagy kódot" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Használj biztonsági kulcsot" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} ellenőrzöttként megjelölve." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Nem sikerült {email}-t ellenőrzöttként megjelölni." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Kijelölt email címek megjelölése ellenőrzöttként" #: account/apps.py:11 msgid "Accounts" msgstr "Felhasználók" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "Email" #: account/fields.py:19 msgid "Email address" msgstr "Email" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" "Adj meg egy telefonszámot az országkóddal együtt (pl. +36 ha Magyarország)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefonszám" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Ugyanazt a jelszót kell megadni mindannyiszor." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Jelszó" #: account/forms.py:67 msgid "Remember Me" msgstr "Emlékezz rám" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Felhasználó azonosító" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Bejelentkezés" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Felhasználónév, email vagy telefonszám" #: account/forms.py:117 msgid "Username or email" msgstr "Felhasználónév vagy email" #: account/forms.py:119 msgid "Username or phone" msgstr "Felhasználónév vagy telefonszám" #: account/forms.py:121 msgid "Email or phone" msgstr "Email vagy telefonszám" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Elfelejtetted a jelszavad?" #: account/forms.py:287 msgid "Email (again)" msgstr "Email (ismét)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Email cím megerősítése" #: account/forms.py:302 msgid "Email (optional)" msgstr "Email (nem kötelező)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Felhasználónév (nem kötelező)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Mindig ugyanazt az email címet kell begépelned." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Jelszó (ismét)" #: account/forms.py:591 msgid "Current Password" msgstr "Jelenlegi jelszó" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Új jelszó" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Új jelszó (ismét)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Kód" #: account/models.py:23 msgid "user" msgstr "felhasználó" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "email cím" #: account/models.py:31 msgid "verified" msgstr "megerősített" #: account/models.py:32 msgid "primary" msgstr "elsődleges" #: account/models.py:38 msgid "email addresses" msgstr "email címek" #: account/models.py:142 msgid "created" msgstr "létrehozva" #: account/models.py:143 msgid "sent" msgstr "elküldve" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "kulcs" #: account/models.py:149 msgid "email confirmation" msgstr "email megerősítés" #: account/models.py:150 msgid "email confirmations" msgstr "email megerősítések" #: headless/apps.py:7 msgid "Headless" msgstr "Fej nélküli" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Felhasználói azonosító megtekintése" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Email cím megtekintése" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Alapvető profil információk megtekintése" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Engedélyek megadása" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "A helyettesítő karakterek nem engedélyezettek, hacsak nincs engedélyezve az " "'URI helyettesítő karakterek engedélyezése'." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "Az URI '{}' egynél több helyettesítő karaktert (*) tartalmaz. URI-nként csak " "egy helyettesítő karakter engedélyezett." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "" "Helyettesítő karakterek csak az URI gazdagépnév részében engedélyezettek." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Engedélyezési kód" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Készülék kód" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Kliens hitelesítő adatok" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Frissítési token" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Titkos" #: idp/oidc/models.py:44 msgid "Public" msgstr "Publikus" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "A kliens által kérhető hatókör(ök). Soronként egy értéket adjon meg, pl.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Ha a kliens nem ad meg hatókört, ezek az alapértelmezett hatókörök kerülnek " "alkalmazásra. Soronként egy értéket adjon meg, pl.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Az engedélyezett engedélytípusok listája. Soronként egy értéket adjon meg, " "pl.: authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Az engedélyezett források listája a cross-origin kérésekhez, soronként egy." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Helyettesítő karakterek (*) engedélyezése az átirányítási URI-kban és a CORS-" "forrásokban. Ha engedélyezve van, az URI-k egyetlen csillagot " "tartalmazhatnak az aldomének egyeztetéséhez." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "A megengedett válasz-típusok listája. Soronként egy érték, pl.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "kliens" #: idp/oidc/models.py:116 msgid "clients" msgstr "kliensek" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Nem adhatsz email címet egy olyan fiókhoz, amit kétlépcsős azonosítás véd." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Nem kapcsolhatod ki a kétlépcsős azonosítást." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Nem generálhatsz helyreállítási kódokat anélkül, hogy a kétlépcsős " "azonosítás be lenne kapcsolva." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Nem aktiválhatod a kétlépcsős azonosítást addig, amíg nem igazolod az email " "címedet." #: mfa/adapter.py:141 msgid "Master key" msgstr "Fő kulcs" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Biztonsági kulcs" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Kulcs sz. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Helyreállítási kódok" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP hitelesítő" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Hitelesítő kód" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Jelszó nélküli" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "A jelszó nélküli működés engedélyezése lehetővé teszi, hogy csak ezzel a " "kulccsal jelentkezz be, de további követelményeket támaszt, mint a " "biometrikus vagy PIN védelem." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Ezzel az email címmel már van fiók létrehozva. Először jelentkezz be abba a " "fiókba, majd kapcsold össze a(z) %s fiókoddal." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Érvénytelen token." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "A fiókodhoz nincs beállítva jelszó." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "A fiókodhoz nem tartozik ellenőrzött email cím." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Nem választhatod le az utolsó megmaradt külső fiókodat." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Ez a külső fiók már egy másik fiókhoz van kapcsolva." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Közösségi Fiókok" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "szolgáltató" #: socialaccount/models.py:53 msgid "provider ID" msgstr "szolgáltató azonosítója" #: socialaccount/models.py:57 msgid "name" msgstr "név" #: socialaccount/models.py:59 msgid "client id" msgstr "kliens azonosító" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "Alkalmazás azonosító, vagy fogyasztói kulcs" #: socialaccount/models.py:64 msgid "secret key" msgstr "titkos kulcs" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API titkos kulcs, kliens titkos kulcs, vagy fogyasztói titkos kulcs" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Kulcs" #: socialaccount/models.py:82 msgid "social application" msgstr "közösségi alkalmazás" #: socialaccount/models.py:83 msgid "social applications" msgstr "közösségi alkalmazások" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "utolsó belépés" #: socialaccount/models.py:121 msgid "date joined" msgstr "csatlakozás dátuma" #: socialaccount/models.py:122 msgid "extra data" msgstr "további adatok" #: socialaccount/models.py:126 msgid "social account" msgstr "közösségi fiók" #: socialaccount/models.py:127 msgid "social accounts" msgstr "közösségi fiókok" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) vagy hozzáférési token (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "titkos kulcs token" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) vagy frissítő token (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "lejárat ideje" #: socialaccount/models.py:175 msgid "social application token" msgstr "közösségi alkalmazás token" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "közösségi alkalmazás tokenek" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Érvénytelen profil adatok" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Bejelentkezés" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Mégsem" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Érvénytelen válasz a hozzáférési kulcs lekérésekor innen: \"%s\". A válasz: " "%s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Érvénytelen válasz a hozzáférési token lekérésekor innen: \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Nincs mentett hozzáférési kulcs ehhez: \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Nincs mentett hozzáférési token ehhez: \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Nincs hozzáférés a privát adatokhoz itt: \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Érvénytelen válasz a hozzáférési kulcs lekérésekor innen: \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Inaktív fiók" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Ez a fiók inaktív." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Egy kódot küldtünk a(z) %(recipient)s címre. A kód csak rövid ideig " "érvényes, ezért kérlek, add meg minél előbb." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Megerősítés" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Új kód kérése" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Hozzáférés megerősítése" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Kérlek hitelesítsd magad újra, hogy megóvd a fiókodat." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Más lehetőségek" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Email ellenőrzése" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Add meg az email ellenőrző kódot" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Másik email cím használata" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Belépés" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Add meg a belépési kódot" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Jelszó visszaállítása" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Add meg a jelszó visszaállító kódot" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Telefonszám ellenőrzés" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Add meg a telefonszám ellenőrző kódot" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Másik telefonszám használata" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Email címek" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "A következő email címek vannak a fiókodhoz rendelve:" #: templates/account/email.html:25 msgid "Verified" msgstr "Ellenőrizve" #: templates/account/email.html:29 msgid "Unverified" msgstr "Nincs ellenőrizve" #: templates/account/email.html:34 msgid "Primary" msgstr "Elsődleges" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Legyen elsődleges" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Ellenőrzés újraküldése" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Eltávolítás" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Email cím hozzáadása" #: templates/account/email.html:70 msgid "Add Email" msgstr "Email hozzáadása" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Valóban el akarod távolítani a kijelölt email címet?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Azért kaptad ezt az emailt, mert te (vagy valaki más) megpróbáltál " "regisztrálni egy fiókot a következő email címmel:\n" "\n" "%(email)s\n" "\n" "Azonban a megadott email címmel már létezik fiók. Amennyiben elfelejtetted a " "jelszavadat, kérjük használd a jelszó visszaállító funkciót a fiókod " "helyreállításához:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "A fiók már létezik" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Üdvözlet a(z) %(site_name)s-tól!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Köszönjük, hogy a(z) %(site_name)s-t használod!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Azért kapod ezt az emailt, mert a következő változtatás történt a fiókodban:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Ha nem ismersz rá erre a változtatásra, kérjük, azonnal tedd meg a megfelelő " "biztonsági óvintézkedéseket. A fiókod innen lett megváltoztatva:\n" "\n" "- IP cím: %(ip)s\n" "- Böngésző: %(user_agent)s\n" "- Dátum: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "" "Az email címed megváltozott a(z) %(from_email)s-ról a(z) %(to_email)s-ra." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Az email megváltozott" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Email címed megerősítve." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Email megerősítése" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Azért kaptad ezt az emailt, mert a(z) %(user_display)s felhasználó megadta " "az email címedet, hogy regisztráljon egy fiókot a(z) %(site_domain)s oldalon." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Az email ellenőrző kódod alább található. Kérlek add meg a nyitott " "böngészőablakban." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "A megerősítéshez menj erre a címre: %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Kérlek, erősítsd meg az email címedet" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "A(z) %(deleted_email)s email cím eltávolítva a fiókodból." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Email eltávolítva" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "A belépési kódod alább található. Kérlek add meg a nyitott böngészőablakban." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Ezt az emailt nyugodtan figyelmen kívül hagyhatod, ha nem te kezdeményezted " "ezt a műveletet." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Belépési kód" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "A jelszavad megváltozott." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Jelszó megváltozott" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "A jelszó visszaállító kódod alább található. Kérlek add meg a nyitott " "böngészőablakban." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Jelszó visszaállító kód" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Azért kaptad ezt az emailt, mert te (vagy valaki más) jelszó visszaállítást " "kértél a fiókodhoz.\n" "Nyugodtan figyelmen kívül hagyhatod ezt a levelet, ha nem te kérted a jelszó " "visszaállítást. A jelszavad visszaállításához kattints az alábbi linkre." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Ha elfelejtetted volna, a felhasználóneved: %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Jelszó visszaállító email" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "A jelszavad visszaállítva." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "A jelszavad beállítva." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Jelszó beállítva" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Azért kapod ezt az emailt, mert te, vagy valaki más megpróbált hozzáférni " "a(z) %(email)s email címmel rendelkező fiókhoz. Azonban az adatbázisunkban " "nincs ilyen fiók." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "Ha te voltál, az alábbi linken regisztrálhatsz egy fiókot." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Ismeretlen fiók" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Email cím" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Jelenlegi email" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Változás erre:" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Az email címed még megerősítésre vár." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Változtatás visszavonása" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Változtatás erre:" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Email csere" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Email cím megerősítése" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Kérlek erősítsd meg, hogy a(z) %(email)s " "email cím a(z) %(user_display)s felhasználóhoz tartozik." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Nem lehet megerősíteni a(z) %(email)s-t, mert már egy másik fiók " "megerősítette." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Ez az email megerősítő link lejárt vagy érvénytelen. Kérlek kérj egy új email megerősítő linket." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Ha még nem hoztál létre fiókot, akkor kérlek, először " "%(link)sregisztrálj%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Belépés passkey-jel" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Küldj nekem egy belépési kódot" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Kijelentkezés" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Biztosan kijelentkezel?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Az elsődleges email cím (%(email)s) nem törölhető." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Megerősítő levelet küldtünk a(z) %(email)s címre." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "A(z) %(email)s cím visszaigazolása megtörtént." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "%(email)s email cím törölve." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Sikeres bejelentkezés, mint %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Sikeresen kijelentkeztél." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Egy belépési kód lett elküldve a(z) %(recipient)s címre." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Jelszó sikeresen megváltoztatva." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Jelszó sikeresen beállítva." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Ellenőrző kódot küldtünk a(z) %(phone)s telefonszámra." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Megerősítetted a(z) %(phone)s telefonszámot." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Elsődleges email cím beállítva." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Jelszócsere" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Elfelejtett jelszó?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Elfelejtetted a jelszavadat? Add meg az email címedet és küldünk egy linket, " "ahol új jelszót kérhetsz." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Új jelszó kérése" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Kérlek vedd fel velünk a kapcsolatot, ha problémád adódik a jelszó " "beállításával." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Elküldtük az ellenőrző emailt.\n" "Ha nem találod a beérkezett levelek között, kérlek nézd meg a spam mappában " "is. Ellenkező esetben vedd fel velünk a kapcsolatot, ha pár percen belül nem " "kapod meg." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Hibás token" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "A új jelszó kérő link érvénytelen volt, vagy már fel lett használva. Itt tudsz újat kérni." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "A jelszavad megváltozott." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Jelszó beállítása" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Telefonszám csere" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Jelenlegi telefonszám" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "A telefonszámod még megerősítésre vár." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Telefonszám megváltoztatása" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Add meg a jelszavadat:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Egy speciális kódot fogsz kapni a jelszó nélküli belépéshez." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Kód kérése" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Egyéb belépési lehetőségek" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Regisztráció" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Regisztrálás" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Van már fiókod? Akkor kérlek %(link)slépj be%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Regisztráció passkey használatával" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Passkey regisztráció" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Egyéb lehetőségek" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Regisztráció lezárva" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Sajnáljuk, de jelenleg nem lehet regisztrálni." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Megjegyzés" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Már bejelentkeztél, mint %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Figyelem:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Jelenleg nem adtál meg egyetlen email címet sem. Nagyon fontos, hogy megadj " "egy email címet, mert csak így kaphatsz értesítéseket és csak így tudod a " "jelszavadat megváltoztatni." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Ellenőrizd az emailedet" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Elküldtük az ellenőrző emailt. A regisztráció befejezéséhez kövesd a benne " "található linket! Kérlek vedd fel velünk a kapcsolatot, ha az email pár " "percen belül nem érkezik meg!" #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Az oldal ezen része megköveteli, hogy ellenőrizzük\n" "a felhasználó azonosságát. Ezért most arra kérünk, \n" "hogy erősítsd meg a hozzáférésedet az email címedhez. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Elküldtük az ellenőrző emailt.\n" "Kérlek, kövesd a benne található linket! Ha nem találod a beérkezett levelek " "között, nézd meg a spam mappában is. Ellenkező esetben vedd fel velünk a " "kapcsolatot, ha pár percen belül nem kapod meg." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Megjegyzés: az email címet még mindig meg tudod változtatni." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Üzenetek:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menü:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Egyéb felhasználói fiókok" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Kétlépcsős azonosítás" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Munkamenetek" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Engedélyezés" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "A(z) %(client_name)s hozzáférést kér a(z) %(site_name)s fiókodhoz." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Eszközkód megadása" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Add meg a készülékeden megjelenő kódot." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Folytatás" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Eszköz megerősítése" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Kérlek erősítsd meg a %(client_name)s-n megjelenő kódot, hogy engedélyezd " "ezt az eszközt." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Elutasítás" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Eszköz engedélyezve" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Sikeresen engedélyezted a(z) %(client_name)s eszközödet." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Eszköz elutasítva" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "A(z) %(client_name)s eszközöd engedélyezése elutasítva." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Hiba" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Bejelentkezve maradás" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "A fiókod kétlépcsős azonosítással védett. Kérlek add meg az azonosító kódot:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "Legeneráltuk az új kétlépcsős azonosító visszaállító kódokat." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Új visszaállító kódok legenerálva" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Azosító alkalmazás aktiválva." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Azonosító alkalmazás aktiválva" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Azosító alkalmazás deaktiválva." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Azonosító alkalmazás deaktiválva" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Egy új biztonsági kulcs lett hozzáadva." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Biztonsági kulcs hozzáadva" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Egy biztonsági kulcs el lett távolítva." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Biztonsági kulcs eltávolítva" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Azonosító alkalmazás" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Azonosítás azonosító alkalmazással aktív." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Azonosító alkalmazás nem aktív." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Kikapcsolás" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Bekapcsolás" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Biztonsági kulcsok" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Hozzáadtál %(count)s biztonsági kulcsot." msgstr[1] "Hozzáadtál %(count)s biztonsági kulcsot." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Nincs hozzáadott biztonsági kulcs." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Kezelés" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Hozzáadás" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Helyreállítási kódok" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "%(unused_count)s van, a rendelkezésre álló %(total_count)s helyreállítási " "kódból." msgstr[1] "" "%(unused_count)s van, a rendelkezésre álló %(total_count)s helyreállítási " "kódból." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Nincs beállított helyreállítási kód." #: templates/mfa/index.html:96 msgid "View" msgstr "Megtekintés" #: templates/mfa/index.html:102 msgid "Download" msgstr "Letöltés" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Generálás" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Új helyreállítási kódok lettek generálva." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Biztonsági kulcs hozzáadva." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Biztonsági kulcs eltávolítva." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Add meg a hitelesítő kódot:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Új helyreállítási kódokat fogsz generálni a fiókodhoz." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Ez a művelet érvényteleníti a meglévő kódjaidat." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Biztos vagy benne?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Felhasználatlan kódok" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Kódok letöltése" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Új kódok generálása" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Hitelesítő alkalmazás bekapcsolása" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Ahhoz, hogy kétlépcsős azonosítással védd a fiókodat, olvasd be az alábbi QR " "kódot a hitelesítő alkalmazásoddal. Ezután add meg az alkalmazás által " "generált ellenőrző kódot." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Hitelesítő titkos kód" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Ezt a titkos kódot elmentheted, és később felhasználhatod a hitelesítő " "alkalmazás újra-telepítésére." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Hitelesítő alkalmazás kikapcsolása" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "A hitelesítő alkalmazáson alapuló azonosítás kikapcsolására készülsz. Biztos " "vagy benne?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Megbízol ebben a böngészőben?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Ha megbízol ebben a böngészőben, a következő belépéskor nem fogunk tőled " "ellenőrző kódot kérni." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Megbízok, a következő időtartamra: %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Nem bízok meg" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Biztonsági kulcs hozzáadása" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Biztonsági kulcs eltávolítása" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Biztos, hogy el akarod távolítani ezt a biztonsági kulcsot?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Használat" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Passkey" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Biztonsági kulcs" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Ez a kulcs nem jelzi, hogy passkey-e" #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Nincs megadva" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Hozzáadva: %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Utoljára használva: %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Szerkesztés" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Biztonsági kulcs szerkesztése" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Mentés" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Passkey létrehozása" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Egy passkey-t fogsz létrehozni a fiókodhoz. Mivel később további kulcsokat " "is hozzáadhatsz, használj egy jól leíró nevet a kulcsok megkülönböztetéséhez." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Létrehozás" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Ez a funkció JavaScriptet igényel." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Külső szolgáltatónál történt bejelentkezési hiba" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "Hiba történt miközben megpróbáltál bejelentkezni a külső fiókoddal." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "Az alábbi külső fiókok bármelyikét használhatod a belépéshez:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Jelenleg nincs külső fiók kapcsolva ehhez a fiókhoz." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Külső fiók hozzáadása" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "" "Egy külső fiók a(z) %(provider)s szolgáltatótól lett kapcsolva a fiókodhoz." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Külső fiók hozzákapcsolva" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" "Egy külső fiók a(z) %(provider)s szolgáltatótól lett leválasztva a fiókodról." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Külső fiók leválasztva" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Kapcsolódás a(z) %(provider)s-hoz" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Egy új %(provider)s fiókot fogsz hozzákapcsolni." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Bejelentkezés a(z) %(provider)s-on keresztül" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "A(z) %(provider)s szolgáltató külső fiókjával fogsz bejelentkezni." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Bejelentkezés megszakítva" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Úgy döntöttél, megszakítod a bejelentkezést az oldalunkra a meglévő fiókjaid " "egyikével. Ha ez nem volt szándékos, kérlek lépj " "be." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Külső fiók hozzákapcsolva." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Külső fiók leválasztva." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "A(z) %(provider_name)s fiókodat fogod használni a(z) %(site_name)s oldalra " "való bejelentkezéshez.\n" "Utolsó lépésként kérlek, töltsd ki az alábbi űrlapot:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Vagy használj egy külső szolgáltatót" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Kijelentkezve az összes többi munkamenetből." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Kezdés időpontja" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP cím" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Böngésző" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Utoljára ekkor volt aktív" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Jelenlegi" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Kijelentkezés a többi munkamenetből" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Felhasználói munkamenetek" #: usersessions/models.py:94 msgid "session key" msgstr "munkamenet kulcs" #~ msgid "Account Connection" #~ msgstr "Egyéb felhasználói fiókok" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "A jelszónak minimum {0} hosszúnak kell lennnie." #~ msgid "The following email address is associated with your account:" #~ msgstr "A következő email címek tartoznak a felhasználódhoz:" #~ msgid "Change Email Address" #~ msgstr "Email cím megváltoztatása" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Kérlek jelentkezz be\n" #~ "az egyik felhasználóddal vagy %(link)sregisztrálj%(end_link)s\n" #~ "új %(site_name)s felhasználót és használd azt:" #~ msgid "or" #~ msgstr "vagy" #~ msgid "OpenID Sign In" #~ msgstr "OpenID bejelentkezés" #~ msgid "This email address is already associated with another account." #~ msgstr "Ez az email cím már hozzá van rendelve egy másik felhasználóhoz." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Elküldtük az emailt. Kérlek vedd fel velünk a kapcsolatot, ha nem kapod " #~ "meg perceken belül." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "A megadott bejelentkezési azonosító vagy a jelszó hibás." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "" #~ "A felhasználói azonosítók csak betűket, számokat és a @/./+/-/_ " #~ "karaktereket tartalmazhatnak." #~ msgid "This username is already taken. Please choose another." #~ msgstr "Ez a felhasználói azonosító már foglalt. Kérlek válassz másikat!" #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "Sikeres visszaigazolás. A(z) %(email)s " #~ "email a(z) %(user_display)s felhasználóhoz tartozik." #~ msgid "Thanks for using our site!" #~ msgstr "Köszönjük, hogy az oldalunkat használod!" ================================================ FILE: allauth/locale/id/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-06-05 15:41+0000\n" "Last-Translator: Nyong Onta 11 \n" "Language-Team: Indonesian \n" "Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.12-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Akun ini sedang tidak aktif." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Anda tidak dapat menghapus alamat email utama Anda." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Alamat e-mail ini sudah terhubung dengan akun ini." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Alamat e-mail dan/atau kata sandi yang anda masukkan tidak benar." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Nomor ponsel dan/atau kata sandi yang anda masukkan tidak benar." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Seorang pengguna sudah terdaftar dengan alamat e-mail ini." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Silahkan ketik kata sandi Anda saat ini." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Kode salah." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Kata sandi salah." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Kunci tidak valid atau kedaluwarsa." #: account/adapter.py:79 msgid "Invalid login." msgstr "login tidak valid." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Token untuk mengatur ulang kata sandi tidak valid." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Anda tidak dapat menambahkan lebih dari %d alamat e-mail." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Seorang pengguna sudah terdaftar dengan nomor telepon ini." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Terlalu banyak percobaan masuk yang gagal. Coba lagi nanti." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Alamat e-mail ini tidak terhubung dengan akun pengguna mana pun." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Nomor telepon ini tidak terhubung dengan akun pengguna mana pun." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Alamat e-mail utama Anda harus diverifikasi." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "" "Name pengguna tidak dapat digunakan. Silahkan gunakan nama pengguna lain." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Nama pengguna dan/atau kata sandi yang anda masukkan tidak benar." #: account/adapter.py:98 msgid "Please select only one." msgstr "Silakan pilih satu saja." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Nilai yang baru harus berbeda dari nilai saat ini." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Bersabarlah, Anda mengirim terlalu banyak permintaan." #: account/adapter.py:826 msgid "Use your password" msgstr "Gunakan kata sandi Anda" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Gunakan aplikasi atau kode autentikator" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Gunakan kunci keamanan" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} ditandai sebagai terverifikasi." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Gagal menandai {email} sebagai terverifikasi." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Tandai alamat email yang dipilih sebagai terverifikasi" #: account/apps.py:11 msgid "Accounts" msgstr "Akun" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-mail" #: account/fields.py:19 msgid "Email address" msgstr "Alamat e-mail" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Masukkan nomor telepon termasuk kode negara (misalnya +1 untuk AS)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telepon" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Anda harus mengetikkan kata sandi yang sama setiap kali." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Kata sandi" #: account/forms.py:67 msgid "Remember Me" msgstr "Ingat Saya" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Nama pengguna" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Masuk" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Nama, email, atau ponsel" #: account/forms.py:117 msgid "Username or email" msgstr "Nama pengguna atau e-mail" #: account/forms.py:119 msgid "Username or phone" msgstr "Nama pengguna atau ponsel" #: account/forms.py:121 msgid "Email or phone" msgstr "E-mail atau telepon" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Lupa kata sandi Anda?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-mail (lagi)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Konfirmasi alamat e-mail" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-mail (opsional)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Nama pengguna (opsional)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Anda harus mengetikkan e-mail yang sama setiap kali." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Kata sandi (lagi)" #: account/forms.py:591 msgid "Current Password" msgstr "Kata sandi saat ini" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Kata sandi baru" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Kata sandi baru (lagi)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Kode" #: account/models.py:23 msgid "user" msgstr "pengguna" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "alamat e-mail" #: account/models.py:31 msgid "verified" msgstr "terverifikasi" #: account/models.py:32 msgid "primary" msgstr "utama" #: account/models.py:38 msgid "email addresses" msgstr "alamat e-mail" #: account/models.py:142 msgid "created" msgstr "dibuat" #: account/models.py:143 msgid "sent" msgstr "dikirim" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "kunci" #: account/models.py:149 msgid "email confirmation" msgstr "konfirmasi e-mail" #: account/models.py:150 msgid "email confirmations" msgstr "konfirmasi e-mail" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Lihat ID pengguna Anda" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Lihat alamat email Anda" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Lihat informasi profil dasar Anda" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Berikan izin" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "Wildcard tidak diizinkan kecuali 'Izinkan wildcard URI' diaktifkan." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' mengandung lebih dari satu wildcard (*). Hanya satu wildcard per " "URI yang diizinkan." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Wildcard hanya diizinkan di bagian hostname URI." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Kode otorisasi" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Kode perangkat" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Kredensial klien" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Token penyegaran" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Rahasia" #: idp/oidc/models.py:44 msgid "Public" msgstr "Publik" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Cakupan yang diizinkan untuk diminta klien. Berikan satu nilai per baris, " "mis.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Jika klien tidak menentukan cakupan, cakupan default ini digunakan. Berikan " "satu nilai per baris, mis.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Daftar jenis grant yang diizinkan. Berikan satu nilai per baris, mis.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Daftar origin yang diizinkan untuk permintaan lintas origin, satu per baris." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Izinkan wildcard (*) di URI redirect dan asal CORS. Saat diaktifkan, URI " "dapat berisi satu tanda bintang untuk mencocokkan subdomain." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Daftar jenis respons yang diizinkan. Berikan satu nilai per baris, mis.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "klien" #: idp/oidc/models.py:116 msgid "clients" msgstr "klien" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Anda tidak dapat menambahkan alamat email ke akun yang dilindungi oleh " "autentikasi dua faktor." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Anda tidak dapat menonaktifkan autentikasi dua faktor." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Anda tidak dapat membuat kode pemulihan tanpa mengaktifkan autentikasi dua " "faktor." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Anda tidak dapat mengaktifkan autentikasi dua faktor hingga Anda " "memverifikasi alamat email Anda." #: mfa/adapter.py:141 msgid "Master key" msgstr "Kunci utama" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Kunci cadangan" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Kunci no. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Kode pemulihan" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "Otentikator TOTP" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Kode autentikator" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Tanpa kata sandi" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Mengaktifkan operasi tanpa kata sandi memungkinkan Anda untuk masuk hanya " "dengan menggunakan kunci ini, tetapi memberlakukan persyaratan tambahan " "seperti perlindungan biometrik atau PIN." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Akun sudah ada dengan alamat email ini. Silakan masuk ke akun tersebut " "terlebih dahulu, lalu hubungkan akun %s Anda." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Token Salah." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Akun Anda tidak memiliki kata sandi." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Akun Anda tidak memiliki alamat e-mail yang terverifikasi." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Anda tidak dapat memutuskan akun pihak ketiga Anda yang terakhir." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Akun pihak ketiga sudah terhubung ke akun yang berbeda." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Akun Sosial" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "pemberi" #: socialaccount/models.py:53 msgid "provider ID" msgstr "ID penyedia" #: socialaccount/models.py:57 msgid "name" msgstr "nama" #: socialaccount/models.py:59 msgid "client id" msgstr "id klien" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "ID Aplikasi, atau kunci konsumen" #: socialaccount/models.py:64 msgid "secret key" msgstr "kunci rahasia" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "Kunci API, kunci klien, atau kunci konsumen" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Kunci" #: socialaccount/models.py:82 msgid "social application" msgstr "aplikasi sosial" #: socialaccount/models.py:83 msgid "social applications" msgstr "aplikasi sosial" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "masuk terakhir" #: socialaccount/models.py:121 msgid "date joined" msgstr "tanggal bergabung" #: socialaccount/models.py:122 msgid "extra data" msgstr "data tambahan" #: socialaccount/models.py:126 msgid "social account" msgstr "akun sosial" #: socialaccount/models.py:127 msgid "social accounts" msgstr "akun sosial" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) atau token akses (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "rahasia token" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) atau token refresh (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "kadaluarsa pada" #: socialaccount/models.py:175 msgid "social application token" msgstr "token aplikasi sosial" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "token-token aplikasi sosial" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Data profil tidak valid" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Masuk" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Batal" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Respons tidak valid saat memperoleh token permintaan dari \"%s\". Responsnya " "adalah: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Respon tidak valid saat meminta token akses dari \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Tidak ada token request yang disimpan untuk \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Tidak ada token akses yang disimpan untuk \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Tidak ada akses ke sumber daya pribadi pada \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Respon tidak valid saat meminta token request dari \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Akun Tidak Aktif" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Akun ini tidak aktif." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Kami telah mengirimkan kode ke %(recipient)s. Kode tersebut akan segera " "kedaluwarsa, jadi harap segera masukkan." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Konfirmasi" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Minta kode baru" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Konfirmasi Akses" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Harap autentikasi ulang untuk menjaga keamanan akun Anda." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Pilihan alternatif" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Verifikasi E-mail" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Masukkan Kode Verifikasi E-mail" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Gunakan alamat e-mail yang berbeda" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Masuk" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Masukkan Kode Masuk" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Reset Kata Sandi" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Masukkan Kode Reset Kata Sandi" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Verifikasi Telepon" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Masukkan Kode Verifikasi Telepon" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Gunakan nomor ponsel berbeda" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Alamat E-mail" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Alamat-alamat e-mail berikut ini terkait dengan akun Anda:" #: templates/account/email.html:25 msgid "Verified" msgstr "Terverifikasi" #: templates/account/email.html:29 msgid "Unverified" msgstr "Tidak Terverifikasi" #: templates/account/email.html:34 msgid "Primary" msgstr "Utama" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Jadikan Utama" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Kirim Ulang Verifikasi" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Hapus" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Tambahkan Alamat E-mail" #: templates/account/email.html:70 msgid "Add Email" msgstr "Tambahkan E-mail" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Apakah Anda benar-benar ingin menghapus alamat e-mail yang dipilih?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Anda menerima email ini karena Anda atau orang lain mencoba mendaftar akun " "menggunakan alamat email:\n" "\n" "%(email)s\n" "\n" "Namun, akun yang menggunakan alamat email tersebut sudah ada. Jika Anda lupa " "tentang hal ini, silakan gunakan prosedur lupa kata sandi untuk memulihkan " "akun Anda:\n" "Akun kamu\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Akun Sudah Ada" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Halo dari %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Terima kasih telah menggunakan %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Anda menerima email ini karena perubahan berikut dilakukan pada akun Anda:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Jika Anda tidak mengenali perubahan ini, harap segera ambil tindakan " "pencegahan keamanan yang tepat. Perubahan pada akun Anda berasal dari:\n" "\n" "- Alamat IP: %(ip)s\n" "- Peramban: %(user_agent)s\n" "- Tanggal: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "E-mail Anda telah diubah dari %(from_email)s menjadi %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "E-mail Diubah" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "E-mail Anda telah dikonfirmasi." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Konfirmasi E-mail" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Anda menerima email ini karena pengguna %(user_display)s telah memberikan " "alamat email Anda untuk mendaftar akun di %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Kode verifikasi e-mail Anda tercantum di bawah ini. Silakan masukkan di " "jendela browser yang terbuka." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Untuk mengonfirmasi kebenarannya, kunjungi %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Mohon Konfirmasi Alamat E-mail Anda" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "Alamat e-mail %(deleted_email)s telah dihapus dari akun Anda." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "E-mail Dihapus" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Kode masuk Anda tercantum di bawah ini. Silakan masukkan di jendela browser " "yang terbuka." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Email ini dapat diabaikan dengan aman jika Anda tidak memulai tindakan ini." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Kode Masuk" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Kata sandi Anda telah diubah." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Kata Sandi Diubah" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Kode reset kata sandi Anda tercantum di bawah ini. Silakan masukkan di " "jendela browser yang terbuka." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Kode Reset Kata Sandi" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Anda menerima e-mail ini karena Anda atau orang lain telah meminta kata " "sandi untuk akun Anda.\n" "Abaikan jika Anda tidak meminta reset kata sandi. Klik link di bawah untuk " "mereset kata sandi Anda." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Jikalau Anda lupa, nama pengguna Anda adalah %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "E-mail Reset Kata Sandi" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Kata sandi Anda telah direset." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Kata sandi Anda telah ditetapkan." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Kata Sandi Ditetapkan" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Anda menerima email ini karena Anda, atau orang lain, mencoba mengakses akun " "dengan email %(email)s. Namun, kami tidak memiliki catatan akun tersebut " "dalam basis data kami." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Jika itu Anda, Anda dapat mendaftar akun menggunakan tautan di bawah ini." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Akun Tidak Dikenal" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Alamat Email" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Email saat ini" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Mengubah ke" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Alamat email Anda masih menunggu verifikasi." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Batalkan Perubahan" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Ubah ke" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Ubah Email" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Konfirmasi Alamat E-mail" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Mohon konfirmasi bahwa %(email)s adalah " "alamat e-mail untuk pengguna %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Tidak dapat mengonfirmasi %(email)s karena sudah dikonfirmasi oleh akun lain." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Tautan konfirmasi e-mail ini sudah kedaluwarsa atau tidak valid. Silakan kirimkan permintaan konfirmasi e-mail baru." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Jika Anda belum membuat akun, mohon %(link)sdaftar%(end_link)s terlebih " "dahulu." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Masuk dengan passkey" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Kirim kode masuk kepada saya" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Keluar" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Anda yakin ingin keluar?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Anda tidak dapat menghapus alamat e-mail utama Anda (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "E-mail konfirmasi dikirim ke %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Anda telah mengkonfirmasi %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Alamat e-mail %(email)s telah dihapus." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Berhasil masuk sebagai %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Anda telah keluar." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Kode masuk telah dikirim ke %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Kata sandi berhasil diubah." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Kata sandi berhasil setel." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Kode verifikasi telah dikirim ke %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Anda telah memverifikasi nomor telepon %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Alamat e-mail utama telah disetel." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Ubah Kata Sandi" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Lupa Kata Sandi?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Lupa kata sandi Anda? Masukkan alamat email Anda di bawah, dan kami akan " "mengirimkan email yang memungkinkan Anda untuk meresetnya." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Reset Kata Sandi Saya" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Mohon hubungi kami jika Anda mengalami masalah saat mengubah kata sandi Anda." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Kami telah mengirimkan email kepada Anda. Jika Anda belum menerimanya, " "silakan periksa folder spam Anda. Jika tidak, hubungi kami jika Anda tidak " "menerimanya dalam beberapa menit." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Token Salah" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Link reset kata sandi tidak valid, mungkin karena telah digunakan. Silakan " "minta reset kata sandi baru." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Kata sandi Anda telah diubah." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Setel Kata Sandi" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Ubah Telepon" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Telepon saat ini" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Nomor telepon Anda masih menunggu verifikasi." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Ubah Telepon" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Masukkan kata sandi Anda:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Anda akan menerima kode khusus untuk masuk tanpa kata sandi." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Minta Kode" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Pilihan masuk lainnya" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Daftar" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Daftar" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Sudah memiliki akun? Silakan %(link)smasuk%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Daftar menggunakan passkey" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Daftar dengan Passkey" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Pilihan lainnya" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Daftar Ditutup" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Mohon maaf, tapi pendaftaran saat ini ditutup." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Catatan" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Anda sudah masuk sebagai %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Peringatan:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Anda saat ini tidak memiliki alamat e-mail yang disetel. Anda sebaiknya " "menambahkan alamat e-mail agar dapat menerima notifikasi, mereset kata " "sandi, dll." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Verifikasi Alamat E-mail Anda" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Kami telah mengirimkan e-mail kepada Anda untuk verifikasi. Ikuti tautan " "yang disediakan untuk menyelesaikan proses pendaftaran. Jika Anda tidak " "melihat e-mail verifikasi di kotak masuk utama, periksa folder spam Anda. " "Silakan hubungi kami jika Anda tidak menerima e-mail verifikasi dalam " "beberapa menit." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Bagian dari situs ini mengharuskan kami untuk memverifikasi bahwa\n" "Anda adalah orang yang Anda klaim. Untuk tujuan ini, kami mengharuskan Anda\n" "memverifikasi kepemilikan alamat e-mail Anda. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Kami telah mengirimkan e-mail kepada Anda untuk\n" "verifikasi. Silakan klik tautan di dalam e-mail tersebut. Jika Anda tidak " "melihat e-mail verifikasi di kotak masuk utama, periksa folder spam Anda. " "Jika tidak,\n" "hubungi kami jika Anda tidak menerimanya dalam beberapa menit." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Catatan: Anda masih bisa mengubah " "alamat e-mail Anda." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Pesan:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menu:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Koneksi Akun" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Autentikasi Dua Faktor" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sesi" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Otorisasi" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s ingin mengakses akun %(site_name)s Anda." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Masukkan Kode Perangkat" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Masukkan kode yang ditampilkan di perangkat Anda." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Lanjutkan" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Konfirmasi Perangkat" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Silakan konfirmasi kode yang ditampilkan pada %(client_name)s Anda untuk " "mengotorisasi perangkat ini." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Tolak" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Perangkat Terotorisasi" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Anda berhasil mengotorisasi perangkat %(client_name)s Anda." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Perangkat Ditolak" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Otorisasi untuk perangkat %(client_name)s Anda telah ditolak." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Kesalahan" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Tetap Masuk" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Akun Anda dilindungi oleh autentikasi dua faktor. Silakan masukkan kode " "autentikator:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "Satu set kode pemulihan Autentikasi Dua Faktor yang baru telah dibuat." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Kode Pemulihan Baru Dibuat" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Aplikasi autentikator diaktifkan." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Aplikasi Autentikator Diaktifkan" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Aplikasi autentikator dinonaktifkan." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Aplikasi Autentikator Dinonaktifkan" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Kunci keamanan baru telah ditambahkan." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Kunci Keamanan Ditambahkan" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Kunci keamanan telah dihapus." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Kunci Keamanan Dihapus" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Aplikasi Autentikator" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Autentikasi menggunakan aplikasi autentikator sedang aktif." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Aplikasi autentikator tidak aktif." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Nonaktifkan" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Aktifkan" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Kunci Keamanan" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Anda telah menambahkan %(count)s kunci keamanan." msgstr[1] "Anda telah menambahkan %(count)s kunci keamanan." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Belum ada kunci keamanan yang ditambahkan." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Kelola" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Tambah" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Kode Pemulihan" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Ada %(unused_count)s dari %(total_count)s kode pemulihan yang tersedia." msgstr[1] "" "Ada %(unused_count)s dari %(total_count)s kode pemulihan yang tersedia." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Belum ada kode pemulihan yang disiapkan." #: templates/mfa/index.html:96 msgid "View" msgstr "Lihat" #: templates/mfa/index.html:102 msgid "Download" msgstr "Unduh" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Buat" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Satu set kode pemulihan baru telah dibuat." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Kunci keamanan ditambahkan." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Kunci keamanan dihapus." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Masukkan kode autentikator:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Anda akan membuat satu set kode pemulihan baru untuk akun Anda." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Tindakan ini akan membatalkan kode Anda yang sudah ada." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Apakah Anda yakin?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Kode yang belum digunakan" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Unduh kode" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Buat kode baru" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Aktifkan Aplikasi Autentikator" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Untuk melindungi akun Anda dengan autentikasi dua faktor, pindai kode QR di " "bawah ini dengan aplikasi autentikator Anda. Kemudian, masukkan kode " "verifikasi yang dihasilkan oleh aplikasi di bawah ini." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Rahasia autentikator" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Anda dapat menyimpan rahasia ini dan menggunakannya untuk menginstal ulang " "aplikasi autentikator Anda di lain waktu." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Nonaktifkan Aplikasi Autentikator" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Anda akan menonaktifkan autentikasi berbasis aplikasi autentikator. Apakah " "Anda yakin?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Percayai Browser Ini?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Jika Anda memilih untuk mempercayai browser ini, Anda tidak akan diminta " "kode verifikasi saat masuk berikutnya." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Percayai selama %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Jangan Percaya" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Tambahkan Kunci Keamanan" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Hapus Kunci Keamanan" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Apakah Anda yakin ingin menghapus kunci keamanan ini?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Penggunaan" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Passkey" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Kunci keamanan" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Kunci ini tidak menunjukkan apakah itu merupakan passkey." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Tidak ditentukan" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Ditambahkan pada %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Terakhir digunakan %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Edit" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Edit Kunci Keamanan" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Simpan" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Buat Passkey" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Anda akan membuat passkey untuk akun Anda. Karena Anda dapat menambahkan " "kunci tambahan nanti, Anda dapat menggunakan nama deskriptif untuk " "membedakan kunci." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Buat" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Fungsi ini memerlukan JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Kegagalan Login Pihak Ketiga" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Terjadi kesalahan saat mencoba masuk menggunakan akun pihak ketiga Anda." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Anda dapat masuk ke akun Anda menggunakan salah satu akun pihak ketiga " "berikut:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "" "Anda saat ini tidak memiliki akun pihak ketiga yang terhubung ke akun ini." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Tambahkan Akun Pihak Ketiga" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Akun pihak ketiga dari %(provider)s telah terhubung ke akun Anda." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Akun Pihak Ketiga Terhubung" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "Akun pihak ketiga dari %(provider)s telah terputus dari akun Anda." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Akun Pihak Ketiga Terputus" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Hubungkan %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Anda akan menghubungkan akun pihak ketiga baru dari %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Masuk Melalui %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "Anda akan masuk menggunakan akun pihak ketiga dari %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Login Dibatalkan" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Anda memutuskan untuk membatalkan masuk ke situs kami menggunakan salah satu " "dari akun Anda. Jika ini adalah kesalahan, silakan lanjutkan ke masuk." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Akun pihak ketiga telah terhubung." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Akun pihak ketiga telah terputus." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Anda akan menggunakan akun %(provider_name)s untuk masuk ke\n" "%(site_name)s. Sebagai langkah akhir, silakan lengkapi formulir berikut:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Atau gunakan pihak ketiga" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Keluar dari semua sesi lainnya." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Dimulai Pada" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "Alamat IP" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Browser" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Terakhir terlihat pada" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Saat ini" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Keluar dari Sesi Lainnya" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Sesi Pengguna" #: usersessions/models.py:94 msgid "session key" msgstr "kunci sesi" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Koneksi Akun" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Kata sandi harus memiliki panjang minimal {0} karakter." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Anda menerima e-mail ini karena Anda atau orang lain telah meminta kata " #~ "sandi untuk akun Anda.\n" #~ "Abaikan jika Anda tidak meminta reset kata sandi. Klik link di bawah " #~ "untuk mereset kata sandi Anda." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "Alamat-alamat e-mail berikut ini terkait dengan akun Anda:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Konfirmasi Alamat E-mail" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Mohon masuk dengan salah satu\n" #~ "dari akun pihak ketiga Anda. Atau, %(link)sdaftar%(end_link)s\n" #~ "untuk akun %(site_name)s dan masuk di bawah:" #~ msgid "or" #~ msgstr "atau" #~ msgid "change password" #~ msgstr "ubah kata sandi" #~ msgid "OpenID Sign In" #~ msgstr "Masuk Dengan OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "Alamat e-mail ini sudah terhubung dengan akun lain." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Kami telah mengirimkan e-mail. Mohon hubungi kami jika Anda tidak " #~ "menerimanya dalam beberapa menit." ================================================ FILE: allauth/locale/it/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Translators: # guglielmo , 2014 # joke2k , 2014 # puntosit , 2014 # Sandro , 2019. msgid "" msgstr "" "Project-Id-Version: django-allauth\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-12-20 19:00+0000\n" "Last-Translator: Gabriele-V \n" "Language-Team: Italian \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.15.1\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Questo account non è attualmente attivo." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Non puoi eliminare il tuo indirizzo email principale." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Questo indirizzo email è già associato a questo account." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "L'indirizzo email e/o la password che hai usato non sono corretti." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Il numero di telefono e/o la password specificati non sono corretti." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Un utente è già registrato con questo indirizzo email." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Inserisci la tua password attuale." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Codice errato." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Password errata." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Chiave non valida o scaduta." #: account/adapter.py:79 msgid "Invalid login." msgstr "Accesso non valido." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Il codice per il reset della password non è valido." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Non puoi aggiungere più di %d indirizzi email." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Un altro utente si è già registrato con questo numero di telefono." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Troppi tentativi di accesso. Riprova più tardi." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "L'indirizzo email non è assegnato a nessun account utente." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Il numero di telefono non è associato a nessun account utente." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Il tuo indirizzo email principale deve essere verificato." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Questo username non può essere usato. Per favore scegline un altro." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Lo username e/o la password che hai usato non sono corretti." #: account/adapter.py:98 msgid "Please select only one." msgstr "Selezionare una sola opzione." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Il nuovo valore deve essere diverso da quello attuale." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Abbi pazienza, stai inviando troppe richieste." #: account/adapter.py:826 msgid "Use your password" msgstr "Usa la tua password" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Usa l'app di autenticazione o un codice" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Usa una chiave di sicurezza" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "Contrassegna {email} come verificato." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Impossibile contrassegnare {email} come verificato." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Contrassegna gli indirizzi email selezionati come verificati" #: account/apps.py:11 msgid "Accounts" msgstr "Accounts" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "email" #: account/fields.py:19 msgid "Email address" msgstr "Indirizzo email" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" "Inserisci un numero di telefono comprensivo di prefisso internazionale (es. " "+39 per l'Italia)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefono" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Devi digitare la stessa password." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Password" #: account/forms.py:67 msgid "Remember Me" msgstr "Ricordami" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Username" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Login" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Nome utente, email o numero di telefono" #: account/forms.py:117 msgid "Username or email" msgstr "Username o email" #: account/forms.py:119 msgid "Username or phone" msgstr "Nome utente o numero di telefono" #: account/forms.py:121 msgid "Email or phone" msgstr "Email o numero di telefono" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Password dimenticata?" #: account/forms.py:287 msgid "Email (again)" msgstr "email (di nuovo)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Conferma dell'indirizzo email" #: account/forms.py:302 msgid "Email (optional)" msgstr "email (opzionale)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Nome utente (opzionale)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Devi digitare la stessa password ogni volta." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Password (nuovamente)" #: account/forms.py:591 msgid "Current Password" msgstr "Password attuale" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Nuova password" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Nuova password (nuovamente)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Codice" #: account/models.py:23 msgid "user" msgstr "utente" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "indirizzo email" #: account/models.py:31 msgid "verified" msgstr "verificato" #: account/models.py:32 msgid "primary" msgstr "primario" #: account/models.py:38 msgid "email addresses" msgstr "indirizzi email" #: account/models.py:142 msgid "created" msgstr "creato" #: account/models.py:143 msgid "sent" msgstr "inviato" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "chiave" #: account/models.py:149 msgid "email confirmation" msgstr "email di conferma" #: account/models.py:150 msgid "email confirmations" msgstr "email di conferma" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Visualizza il tuo ID utente" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Visualizza il tuo indirizzo email" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Visualizza le informazioni di base del tuo profilo" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Concedi autorizzazioni" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "I caratteri jolly non sono consentiti a meno che non sia abilitata l'opzione " "'Consenti caratteri jolly negli URI'." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "L'URI '{}' contiene più di un carattere jolly (*). È consentito un solo " "carattere jolly per URI." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "" "I caratteri jolly sono consentiti solo nella parte del nome host dell'URI." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Codice di autorizzazione" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Codice dispositivo" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Credenziali client" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Aggiorna token" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Confidenziale" #: idp/oidc/models.py:44 msgid "Public" msgstr "Pubblico" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Gli ambiti che il client è autorizzato a richiedere. Fornire un valore per " "riga, ad esempio: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Se il client non specifica alcun ambito, vengono utilizzati questi ambiti " "predefiniti. Fornire un valore per riga, ad esempio: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Elenco dei tipi di autorizzazione consentiti. Fornire un valore per riga, ad " "esempio: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Elenco delle origini consentite per le richieste cross-origin, una per riga." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Consenti caratteri jolly (*) negli URI di reindirizzamento e nelle origini " "CORS. Quando abilitato, gli URI possono contenere un singolo asterisco per " "corrispondere ai sottodomini." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Elenco dei tipi di risposta consentiti. Fornire un valore per riga, ad " "esempio: code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "client" #: idp/oidc/models.py:116 msgid "clients" msgstr "clients" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Non è possibile aggiungere un indirizzo email a un account protetto da " "autenticazione a due fattori." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Non è possibile disattivare l'autenticazione a due fattori." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Non è possibile creare dei codici di recuperazione senza avere attivato " "l'autenticazione a due fattori." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Non è possibile attivare l'autenticazione a due fattori fino a quando non " "hai verificato il tuo indirizzo email." #: mfa/adapter.py:141 msgid "Master key" msgstr "Chiave principale" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Chiave di backup" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Chiave numero {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Codici di recupero" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "Autenticatore TOTP" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Codice autenticatore" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Senza password" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "L'abilitazione dell'operazione senza password ti consente di accedere " "utilizzando solo questa chiave, ma impone requisiti aggiuntivi come l'uso di " "dati biometrici o la protezione tramite PIN." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Esiste già un account con questo indirizzo email. Per favore entra con " "quell'account, e successivamente connetti il tuo account %s." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Token non valido." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Il tuo account non ha ancora nessuna password." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Il tuo account non ha un indirizzo email verificato." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "" "Non puoi scollegare il tuo ultimo servizio di autenticazione Social Network." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "L'account di terze parti è già collegato a un altro account." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Account" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "provider" #: socialaccount/models.py:53 msgid "provider ID" msgstr "ID del Provider" #: socialaccount/models.py:57 msgid "name" msgstr "nome" #: socialaccount/models.py:59 msgid "client id" msgstr "Id cliente" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "App ID, o consumer key" #: socialaccount/models.py:64 msgid "secret key" msgstr "chiave segreta" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "Segreto API, segreto del cliente, or segreto del consumatore" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Chiave" #: socialaccount/models.py:82 msgid "social application" msgstr "applicazione sociale" #: socialaccount/models.py:83 msgid "social applications" msgstr "applicazioni sociali" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "Ultimo accesso" #: socialaccount/models.py:121 msgid "date joined" msgstr "data iscrizione" #: socialaccount/models.py:122 msgid "extra data" msgstr "dati aggiuntivi" #: socialaccount/models.py:126 msgid "social account" msgstr "account sociale" #: socialaccount/models.py:127 msgid "social accounts" msgstr "account sociali" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) o token di accesso (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "token segreto" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) o token di aggiornamento (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "scade il" #: socialaccount/models.py:175 msgid "social application token" msgstr "token dell'applicazione social" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "token dell'applicazione social" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Dati profilo non validi" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Login" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Annulla" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Risposta non valida durante l'ottenimento del token di richiesta da \\\"%s\\" "\". La risposta è stata: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Risposta non valida alla richiesta di un token da \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Nessuna richiesta di token salvata per \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Nessun token di accesso salvato per \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Nessuna accesso alle risorse private a \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Risposta non valida alla richiesta di un token da \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Account non attivo" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Questo account non è attivo." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Abbiamo inviato un codice a %(recipient)s. Il codice scadrà a breve, quindi " "inseriscilo al più presto." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Conferma" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Richiedi nuovo codice" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Conferma l'accesso" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Si prega di rieseguire l'autenticazione per proteggere il tuo account." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Opzioni alternative" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Verifica Email" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Inserisci il codice di verifica email" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Utilizza un indirizzo email differente" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Accedi" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Inserisci il codice di accesso" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Reimpostazione password" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Inserisci il codice di reimpostazione della password" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Verifica del Numero di Telefono" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Inserisci il Codice di Verifica del Telefono" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Utilizza un numero di telefono diverso" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Indirizzi email" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "I seguenti indirizzi email sono associati al tuo account:" #: templates/account/email.html:25 msgid "Verified" msgstr "Verificato" #: templates/account/email.html:29 msgid "Unverified" msgstr "Non verificato" #: templates/account/email.html:34 msgid "Primary" msgstr "Principale" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Rendi Principale" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Re-invia la Verifica" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Rimuovi" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Aggiungi un indirizzo email" #: templates/account/email.html:70 msgid "Add Email" msgstr "Aggiungi email" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Sei sicuro di voler rimuovere l'indirizzo email selezionato?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Stai ricevendo questa email perché tu o qualcun altro\n" "avete tentato di registrare un account utilizzando l'indirizzo email: \n" "\n" "%(email)s \n" "\n" "Tuttavia, esiste già un account associato a questo indirizzo email. Se lo " "hai dimenticato,\n" "utilizza la procedura di recupero password\n" "per recuperare il tuo account: \n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "L'account esiste già" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Ciao da %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Grazie per usare %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Stai ricevendo questa email perché è stata apportata la seguente modifica al " "tuo account:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Se non riconosci questa modifica, adotta immediatamente le opportune misure " "di sicurezza. La modifica al tuo account proviene da:\n" "\n" "- Indirizzo IP: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Data: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "La tua email è stata cambiata da %(from_email)s a %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Email modificata" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "La tua mail è stata confermata." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Manda una conferma" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Hai ricevuto questa email perché l'utente %(user_display)s ha fornito il tuo " "indirizzo email per registrare un account su %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Il tuo codice di verifica email è riportato di seguito. Inseriscilo nella " "finestra del browser aperta." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Per confermare che sia corretto, vai su %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Conferma il tuo indirizzo email" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "L'indirizzo email %(deleted_email)s è stato rimosso dal tuo account." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Email rimossa" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Il tuo codice di accesso è riportato di seguito. Inseriscilo nella finestra " "del browser aperta." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Puoi ignorare questa email in sicurezza se non hai avviato questa azione." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Codice di connessione" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "La tua password è stata cambiata." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Password cambiata" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Il tuo codice di reimpostazione della password è riportato di seguito. " "Inseriscilo nella finestra del browser aperta." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Codice di reimpostazione della password" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Hai ricevuto questa mail perché hai richiesto la password per il tuo account " "utente.\n" "Se non hai richiesto tu il reset della password, ignora questa mail, " "altrimenti clicca sul link qui sotto per fare il reset della password." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Nel caso tu lo abbia dimenticato, il tuo nome utente è %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Email per reimpostare la password" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "La tua password è stata reimposta." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Password impostata." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Imposta la Password" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Stai ricevendo questa email perché tu, o qualcun altro, ha tentato di " "accedere a un account con l'email %(email)s. Tuttavia, non abbiamo alcun " "record di un account con questa email nel nostro database." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Se sei stato tu, puoi registrarti per un account utilizzando il link qui " "sotto." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Account sconosciuto" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Indirizzo email" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Email attuale" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Passare a" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "La tua email è ancora in attesa di verifica." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Annulla Modifica" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Passa a" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Cambia Email" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Conferma l'indirizzo email" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Conferma che %(email)s è un indirizzo email " "per l'utente %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Impossibile confermare %(email)s perché è già stata confermata da un account " "diverso." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Questo link di conferma email è scaduto o non è valido. Ti chiediamo di ripetere la richiesta di conferma via email." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "Se non hai ancora creato un account, %(link)sRegistrati%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Accedi con una passkey" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Inviami un codice di accesso" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Disconnetti" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Sei sicuro di volerti disconnettere?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Non puoi eliminare il tuo indirizzo email principale (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Email di conferma inviata a %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Hai appena confermato l’indirizzo email %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Indirizzo email %(email)s rimosso." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Ti sei collegato con successo come %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Ti sei scollegato." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Un codice di accesso è stato inviato a %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Password cambiata con successo." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Password impostata correttamente." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Un codice di verifica è stato inviato a %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Hai verificato il numero di telefono %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Indirizzo email principale impostato." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Cambia la tua Password" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Password dimenticata?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Hai dimenticato la tua password? Inserisci qui sotto il tuo indirizzo email, " "ti invieremo una mail con un link per reimpostarla." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Reimposta la mia password" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "Se hai qualche problema a reimpostare la password, contattaci." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Ti abbiamo inviato un'email. Se non l'hai ricevuta, controlla la cartella " "dello spam. In caso contrario, contattaci se non la ricevi entro pochi " "minuti." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Token non valido" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Il link di reimpostazione della password non è valido, probabilmente è già " "stato usato. Inoltra una nuova richiesta di " "reimpostazione della password." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Password cambiata." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Imposta una password" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Cambia Numero di Telefono" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Numero di telefono attuale" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Il tuo numero di telefono è ancora in attesa di verifica." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Cambia Numero di Telefono" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Inserisci la tua password:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Riceverai un codice speciale per un accesso senza password." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Richiedi Codice" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Altre opzioni di accesso" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Registrati" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Registrazione" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Hai già un account valido? %(link)sAccedi%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Registrati con una passkey" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Registrazione con una chiave di sicurezza" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Altre opzioni" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Registrazioni Chiuse" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Spiacenti, le registrazioni sono per il momento sospese." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Nota" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Sei già collegato come %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Attenzione:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Al momento non hai configurato alcun indirizzo email. Ti consigliamo " "vivamente di aggiungere un indirizzo email per poter ricevere notifiche, " "reimpostare la password, ecc." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Verifica il tuo indirizzo email" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Ti abbiamo inviato un'email di verifica. Segui il link fornito per " "completare la procedura di registrazione. Se non trovi l'email di verifica " "nella tua casella di posta principale, controlla la cartella dello spam. " "Contattaci se non ricevi l'email di verifica entro pochi minuti." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Per utilizzare questa parte del sito dobbiamo verificare\n" "che sei veramente chi dici di essere. Sarà sufficiente\n" "dimostrare che hai effettivamente accesso al tuo indirizzo email. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Ti abbiamo inviato un'email di verifica.\n" "Clicca sul link contenuto nell'email. Se non vedi l'email di verifica nella " "tua casella di posta principale, controlla la cartella dello spam.\n" "In caso contrario, contattaci se non la ricevi entro pochi minuti." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Nota: puoi comunque modificare il " "tuo indirizzo email." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Messaggi:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menu:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Connessioni all'account" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Autenticazione a Due Fattori" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sessioni" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Autorizza" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s desidera accedere al tuo account %(site_name)s." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Inserisci il codice dispositivo" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Inserisci il codice visualizzato sul tuo dispositivo." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Continua" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Conferma dispositivo" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Conferma il codice visualizzato sul tuo %(client_name)s per autorizzare " "questo dispositivo." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Nega" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Dispositivo autorizzato" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Hai autorizzato con successo il tuo dispositivo %(client_name)s." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Dispositivo negato" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "" "L'autorizzazione per il tuo dispositivo %(client_name)s è stata negata." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Errore" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Resta connesso" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Il tuo account è protetto da autenticazione a due fattori. Inserisci il " "codice di autenticazione:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "È stato generato un nuovo set di codici di recupero per l'Autenticazione a " "Due Fattori." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Nuovi Codici di Recupero Generati" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Applicazione di autenticazione attivata." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Applicazione di autenticazione attivata" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Applicazione di autenticazione disattivata." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Applicazione di autenticazione disattivata" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "È stata aggiunta una nuova chiave di sicurezza." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Chiave di sicurezza aggiunta" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Una chiave di sicurezza è stata rimossa." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Chiave di sicurezza rimossa" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Applicazione di autenticazione" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "L'autenticazione tramite un'applicazione di autenticazione è attiva." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Un'applicazione di autenticazione non è attiva." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Disattiva" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Attiva" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Chiavi di sicurezza" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Hai aggiunto %(count)s chiave di sicurezza." msgstr[1] "Hai aggiunto %(count)s chiavi di sicurezza." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Nessuna chiave di sicurezza è stata aggiunta." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Gestisci" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Aggiungi" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Codici di recupero" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "C'è %(unused_count)s codice di recupero disponibile su un totale di " "%(total_count)s." msgstr[1] "" "Ci sono %(unused_count)s codici di recupero disponibili su un totale di " "%(total_count)s." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Nessun codice di recupero impostato." #: templates/mfa/index.html:96 msgid "View" msgstr "Visualizza" #: templates/mfa/index.html:102 msgid "Download" msgstr "Scarica" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Genera" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "È stato generato un nuovo set di codici di recupero." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Chiave di sicurezza aggiunta." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Chiave di sicurezza rimossa." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Inserisci un codice dell'autenticatore:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "" "Stai per generare un nuovo set di codici di recupero per il tuo account." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Questa azione renderà invalidi i tuoi codici esistenti." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Sei sicuro?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Codici non utilizzati" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Scarica codici" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Genera nuovi codici" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Attiva l'applicazione di autenticazione" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Per proteggere il tuo account con l'autenticazione a due fattori, " "scannerizza il codice QR qui sotto con la tua applicazione di " "autenticazione. Successivamente, inserisci il codice di verifica generato " "dall'applicazione qui sotto." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Segreto dell'autenticatore" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Puoi conservare questo segreto e utilizzarlo per reinstallare la tua " "applicazione di autenticazione in un momento successivo." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Disattiva l'applicazione di autenticazione" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Stai per disattivare l'autenticazione basata sull'applicazione di " "autenticazione. Sei sicuro?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Ti fidi di questo browser?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Se scegli di fidarti di questo browser, non ti verrà richiesto alcun codice " "di verifica al prossimo accesso." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Fiducia per %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Non fidarti" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Aggiungi Chiave di Sicurezza" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Rimuovi Chiave di Sicurezza" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Sei sicuro di voler rimuovere questa chiave di sicurezza?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Utilizzazione" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Chiave di sicurezza" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Chiave di sicurezza" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Questa chiave non indica se è una passkey." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Non specificato" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Aggiunta a %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Ultimo utilizzo: %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Modifica" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Modifica Chiave di Sicurezza" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Salva" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Crea Passkey" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Stai per creare una passkey per il tuo account. Poiché puoi aggiungere " "ulteriori chiavi in seguito, puoi utilizzare un nome descrittivo per " "distinguerle." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Crea" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Questa funzionalità richiede JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Errore di accesso con account di terze parti" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Si è verificato un errore durante il tentativo di accesso con il tuo account " "di terze parti." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Puoi collegarti al tuo account utilizzando uno dei seguenti servizi di " "autenticazione Social Network:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Attualmente non hai account di terze parti collegati a questo account." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Aggiungi un account di un Social Network" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "" "Un account di terze parti da %(provider)s è stato collegato al tuo account." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Account di terze parti collegato" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" "Un account di terze parti da %(provider)s è stato disconnesso dal tuo " "account." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Account di terze parti disconnesso" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Connetti %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Stai per collegare un nuovo account di terze parti da %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Accedi tramite %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" "Stai per accedere utilizzando un account di terze parti da %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Accesso annullato" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Hai deciso di cancellare l'accesso a questo sito usando uno dei tuoi account " "attivi. Se è stato un errore, ripeti l'Accesso." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "L'account di terze parti è stato collegato." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "L'account di terze parti è stato disconnesso." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Stai per usare il tuo account su %(provider_name)s per effettuare il login " "su\n" "%(site_name)s. Come ultima operazione ti chiediamo di riempire il form qui " "sotto:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "O usa un servizio di terze parti" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Disconnesso da tutte le altre sessioni." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Iniziato alle" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "Indirizzo IP" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Browser" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Ultimo accesso alle" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Attuale" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Esci dalle altre sessioni" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Sessioni utente" #: usersessions/models.py:94 msgid "session key" msgstr "chiave di sessione" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Connessioni all'account" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "La password deve essere lunga almeno {0} caratteri." #, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Stai ricevendo questa email perché tu o qualcun altro ha richiesto una\n" #~ "password per il tuo account utente. Tuttavia, non abbiamo alcun record di " #~ "un utente\n" #~ "con l'indirizzo email %(email)s nel nostro database.\n" #~ "\n" #~ "Questa email può essere ignorata in modo sicuro se non hai richiesto un " #~ "ripristino della password.\n" #~ "\n" #~ "Se sei stato tu, puoi registrarti per un account utilizzando il link qui " #~ "sotto." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "I seguenti indirizzi email sono associati al tuo account:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Conferma l'Indirizzo Email" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Per favore, accedi con uno\n" #~ "dei tuoi account social, o %(link)sregistra%(end_link)s\n" #~ "il tuo account per %(site_name)s e accedi:" #~ msgid "or" #~ msgstr "o" #~ msgid "change password" #~ msgstr "cambia password" #~ msgid "OpenID Sign In" #~ msgstr "Accesso con OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "Questo indirizzo email è gia associato a un altro account." #~ msgid "" #~ "We have sent you an email. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Ti abbiamo spedito una mail. Contattaci se non la ricevi entro qualche " #~ "minuto." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "Il login e/o la password che hai usato non sono corretti." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "Gli username possono contenere solo lettere, cifre e @/./+/-/_." #~ msgid "This username is already taken. Please choose another." #~ msgstr "Questo username è già in uso. Per favore scegline un altro." #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "email address for user %(user_display)s." #~ msgstr "" #~ "Hai appena confermato che %(email)s è un " #~ "indirizzo email valido per l'utente %(user_display)s." #~ msgid "Thanks for using our site!" #~ msgstr "Grazie per aver utilizzato questo Sito!" ================================================ FILE: allauth/locale/ja/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-08-07 02:02+0000\n" "Last-Translator: SATOH Fumiyasu \n" "Language-Team: Japanese \n" "Language: ja\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 5.13-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "このアカウントは現在無効です。" #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "メインのメールアドレスは削除できません。" #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "このメールアドレスはすでに別のアカウントで登録されています。" #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "入力されたメールアドレスもしくはパスワードが正しくありません。" #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "入力された電話番号もしくはパスワードが正しくありません。" #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "ほかのユーザーがこのメールアドレスでアカウント登録済みです。" #: account/adapter.py:75 msgid "Please type your current password." msgstr "現在のパスワードを入力してください。" #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "コードが正しくありません。" #: account/adapter.py:77 msgid "Incorrect password." msgstr "パスワードが正しくありません。" #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "不正または期限切れのキーです。" #: account/adapter.py:79 msgid "Invalid login." msgstr "不正なログインです。" #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "パスワード再設定コードが無効です。" #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "メールアドレスは %d 個までしか登録できません。" #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "ほかのユーザーがこの電話番号を登録済みです。" #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "ログイン失敗が連続しています。時間が経ってからやり直してください。" #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "このメールアドレスで登録されたユーザーアカウントがありません。" #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "この電話番号で登録されたユーザーアカウントがありません。" #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "メインのメールアドレスは確認済みでなければいけません。" #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "このユーザー名は使用できません。ほかのユーザー名を選んでください。" #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "入力されたユーザー名もしくはパスワードが正しくありません。" #: account/adapter.py:98 msgid "Please select only one." msgstr "1つだけ選択してください。" #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "現在値と異なる値にしてください。" #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "あなたは大量のリクエストを送信しています。しばらくお待ちください。" #: account/adapter.py:826 msgid "Use your password" msgstr "パスワードを使用する" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "認証アプリまたは認証コードを使用する" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "セキュリティキーを使用する" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} を確認済みにしました。" #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "{email} を確認済みに変更する処理が失敗しました。" #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "確認済みにするメールアドレスを選択" #: account/apps.py:11 msgid "Accounts" msgstr "アカウント" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "メールアドレス" #: account/fields.py:19 msgid "Email address" msgstr "メールアドレス" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "電話番号を国番号付きで入力してください (日本なら国番号 +81)。" #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "電話番号" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "同じパスワードを入力してください。" #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "パスワード" #: account/forms.py:67 msgid "Remember Me" msgstr "ログインしたままにする" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "ユーザー名" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "ログイン" #: account/forms.py:115 msgid "Username, email or phone" msgstr "ユーザー名、メールアドレスまたは電話番号" #: account/forms.py:117 msgid "Username or email" msgstr "ユーザー名またはメールアドレス" #: account/forms.py:119 msgid "Username or phone" msgstr "ユーザー名または電話番号" #: account/forms.py:121 msgid "Email or phone" msgstr "メールアドレスまたは電話番号" #: account/forms.py:144 msgid "Forgot your password?" msgstr "パスワードをお忘れですか?" #: account/forms.py:287 msgid "Email (again)" msgstr "メールアドレス (確認用)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "メールアドレスの確認" #: account/forms.py:302 msgid "Email (optional)" msgstr "メールアドレス (オプション)" #: account/forms.py:314 msgid "Username (optional)" msgstr "ユーザー名 (オプション)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "同じパスワードを入力してください。" #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "パスワード (再入力)" #: account/forms.py:591 msgid "Current Password" msgstr "現在のパスワード" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "新しいパスワード" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "新しいパスワード (再入力)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "コード" #: account/models.py:23 msgid "user" msgstr "ユーザー" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "メールアドレス" #: account/models.py:31 msgid "verified" msgstr "確認済み" #: account/models.py:32 msgid "primary" msgstr "メイン" #: account/models.py:38 msgid "email addresses" msgstr "メールアドレス" #: account/models.py:142 msgid "created" msgstr "作成日時" #: account/models.py:143 msgid "sent" msgstr "送信日時" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "キー" #: account/models.py:149 msgid "email confirmation" msgstr "メールアドレスの確認" #: account/models.py:150 msgid "email confirmations" msgstr "メールアドレスの確認" #: headless/apps.py:7 msgid "Headless" msgstr "ヘッドレス" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "あなたのユーザー ID を表示" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "あなたのメールアドレスを表示" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "あなたの基本ユーザー情報を表示" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "認可パーミッション" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "「URIワイルドカードを許可」が有効でない限り、ワイルドカードは使用できません。" #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI「{}」に複数のワイルドカード(*)が含まれています。URIごとに使用できるワイ" "ルドカードは1つのみです。" #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "ワイルドカードはURIのホスト名部分でのみ使用できます。" #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "認可コード" #: idp/oidc/models.py:38 msgid "Device code" msgstr "デバイスコード" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "クライアントクレデンシャル" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "リフレッシュトークン" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "クレデンシャル" #: idp/oidc/models.py:44 msgid "Public" msgstr "公開" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "許可するクライアントスコープ。行ごとに区切ってスコープを入力してください。" "(例: openid(改行)profile(改行)email(改行))" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "クライアントがスコープを指定しない場合、この既定のスコープが使用されます。行" "ごとに区切って入力してください。(例: openid(改行)profile(改行)email(改行))" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "許可する認可タイプの一覧。行ごとに区切って入力してください。(例: " "authorization_code(改行)client_credentials(改行)refresh_token(改行))" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "オリジン間リクエストで許可するリクエスト元の一覧。行ごとに区切って入力してく" "ださい。" #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "リダイレクトURIおよびCORSオリジンでワイルドカード(*)を許可します。有効にす" "ると、URIにサブドメインに一致する単一のアスタリスクを含めることができます。" #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "許可する応答タイプの一覧。行ごとに区切って入力してください。(例: code(改" "行)id_token token(改行))" #: idp/oidc/models.py:115 msgid "client" msgstr "クライアント" #: idp/oidc/models.py:116 msgid "clients" msgstr "クライアント" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "多要素認証で保護されているアカウントには、メールアドレスを追加できません。" #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "多要素認証は無効化できません。" #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "リカバリーキーを生成するには多要素認証の有効化が必要です。" #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "多要素認証を有効化するには、先にメールアドレスの確認を完了させてください。" #: mfa/adapter.py:141 msgid "Master key" msgstr "マスターキー" #: mfa/adapter.py:143 msgid "Backup key" msgstr "バックアップキー" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "キー番号 {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "多要素認証" #: mfa/models.py:24 msgid "Recovery codes" msgstr "リカバリーコード" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP 認証アプリ" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "認証アプリコード" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "パスワードレス" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "パスワードレス機能を有効化すると、このキーを使用してログイン可能になります。" "その代わりに生態認証や PIN コードなどによる保護が必要になります。" #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "ほかのアカウントが既にこのメールアドレスを登録しています。そのアカウントでロ" "グインしてから %s アカウントを接続してください。" #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "不正なトークンです。" #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "アカウントにパスワードを設定する必要があります。" #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "確認済みのメールアドレスの登録が必要です。" #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "唯一残っている外部アカウントとの接続は解除できません。" #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "この外部アカウントはほかのアカウントに接続されています。" #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "外部アカウント" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "プロバイダー" #: socialaccount/models.py:53 msgid "provider ID" msgstr "プロバイダー ID" #: socialaccount/models.py:57 msgid "name" msgstr "名前" #: socialaccount/models.py:59 msgid "client id" msgstr "クライアント ID" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "App ID もしくはコンシューマキー" #: socialaccount/models.py:64 msgid "secret key" msgstr "シークレットキー" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "" "API シークレット、クライアントシークレット、またはコンシューマーシークレット" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "キー" #: socialaccount/models.py:82 msgid "social application" msgstr "ソーシャルアプリケーション" #: socialaccount/models.py:83 msgid "social applications" msgstr "ソーシャルアプリケーション" #: socialaccount/models.py:118 msgid "uid" msgstr "UID" #: socialaccount/models.py:120 msgid "last login" msgstr "最終ログイン" #: socialaccount/models.py:121 msgid "date joined" msgstr "アカウント作成日" #: socialaccount/models.py:122 msgid "extra data" msgstr "エクストラデータ" #: socialaccount/models.py:126 msgid "social account" msgstr "ソーシャルアカウント" #: socialaccount/models.py:127 msgid "social accounts" msgstr "ソーシャルアカウント" #: socialaccount/models.py:161 msgid "token" msgstr "トークン" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "「oauth_token」(OAuth1) もしくは Access Token (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "トークンシークレット" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "「oauth_token_secret」(OAuth1) もしくは Refresh Token (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "失効期限" #: socialaccount/models.py:175 msgid "social application token" msgstr "ソーシャルアプリケーショントークン" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "ソーシャルアプリケーショントークン" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "無効なプロファイルデータ" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "ログイン" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "キャンセル" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "リクエストトークン取得中に「%s」から不正な応答がありました。レスポンス: %s" #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "" "不正なレスポンスが返されたため、「%s」からアクセストークンを取得できませんで" "した。" #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "「%s」のリクエストトークンが保存されていません。" #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "「%s」のアクセストークンが保存されていません。" #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "「%s」の情報にアクセスできませんでした。" #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "" "不正なレスポンスが返されたため、「%s」からリクエストトークンを取得できません" "でした。" #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "無効なアカウント" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "このアカウントは無効です。" #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "コードを %(recipient)s に送信しました。コードの有効期限はまもなく切れますの" "で、お早めにご入力ください。" #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "確認する" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "新しいコードを要求" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "アクセス確認" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "アカウントを保護するために再認証してください。" #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "そのほかの方法" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "メールアドレスの確認" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "メールの確認コードを入力:" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "別のメールアドレスを使用する" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "ログイン" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "ログインコードを入力" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "パスワードの再設定" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "パスワード再設定コードの入力" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "電話番号の確認" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "電話番号の確認コードの入力" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "別の電話番号を使用する" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "メールアドレス" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "以下のメールアドレスがアカウントに登録されています:" #: templates/account/email.html:25 msgid "Verified" msgstr "確認済み" #: templates/account/email.html:29 msgid "Unverified" msgstr "未確認" #: templates/account/email.html:34 msgid "Primary" msgstr "メイン" #: templates/account/email.html:44 msgid "Make Primary" msgstr "メインにする" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "確認メールを再送する" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "削除" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "メールアドレスの登録" #: templates/account/email.html:70 msgid "Add Email" msgstr "メールアドレスの登録" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "選択されたメールアドレスを削除してもよろしいですか?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "あなた (もしくはほかの誰か) が次のメールアドレスを使用してアカウントの登録を" "試みました:\n" "\n" "%(email)s\n" "\n" "しかし、このメールアドレスを使用したアカウントがすでに存在しています。もしア" "カウントの登録を忘れていた場合は、パスワードを忘れたときの手続きを利用してア" "カウントを回復してください:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "アカウントが既に存在" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "こんにちは、%(site_name)s です!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "%(site_name)s をご利用いただきありがとうございます!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "あなたのアカウントに以下の変更が加えられたため、このメールが届いています:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "この変更が認識できない場合は、直ちに適切なセキュリティ対策を講じてください。" "あなたのアカウントに対するこの変更の起源は以下の通りです:\n" "\n" "- IPアドレス: %(ip)s\n" "- ブラウザ: %(user_agent)s\n" "- 日付: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "" "あなたのメールアドレスを %(from_email)s から %(to_email)s に変更しました。" #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "メールアドレスを変更しました" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "あなたのメールアドレスを確認しました。" #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "メールアドレスの確認" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "%(user_display)s さんが %(site_domain)s にあなたのメールアドレスを登録しよう" "としています。" #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "あなたのメール確認コードを下記に記載します。開いているブラウザのウィンドウに" "入力してください。" #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "間違いなければ、%(activate_url)s にアクセスしてください。" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "メールアドレスを確認してください" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "" "メールアドレス %(deleted_email)s をあなたのアカウントから削除しました。" #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "メールアドレスの削除" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "以下にあなたのログインコードが記載されています。開いているブラウザのウィンド" "ウに入力してください。" #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "この操作があなたによるものではない場合、このメールは無視しても差し支えありま" "せん。" #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "ログインコード" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "パスワードを変更しました。" #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "パスワードを変更しました" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "以下にあなたのパスワード再設定コードを記載します。開いているブラウザのウィン" "ドウに入力してください。" #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "パスワード再設定コード" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "このメールは、あなた (もしくはほかの誰か) がパスワードの再設定を行おうとした" "ために送られました。\n" "パスワードの再設定を要求したのがあなたではない場合、このメールは無視してくだ" "さい。\n" "パスワードを再設定するためには、以下のリンクをクリックしてください。" #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "あなたのアカウントのユーザー名は %(username)s です。" #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "パスワード再設定メール" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "パスワードを再設定しました。" #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "パスワードを設定しました。" #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "パスワード設定" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "あなた (もしくはほかの誰か) がメールアドレス %(email)s を持つアカウントにアク" "セスしようとしたため、この通知メールを送りました。ただし、私たちのデータベー" "スには該当するアカウント情報がありませんでした。" #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "もしあなたで間違いないなら、以下のリンクからアカウントを登録できます。" #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "不明なアカウント" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "メールアドレス" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "現在のメールアドレス" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "変更中" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "あなたのメールアドレスはまだ確認されていません。" #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "変更をキャンセル" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "変更する" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "メールアドレス変更" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "メールアドレスの確認" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "メールアドレス %(email)s がユーザー " "%(user_display)s さんのものであることを確認してください。" #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "%(email)s はすでにほかのアカウントで確認済みです。" #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "メールアドレス確認用のリンクが不正か、期限が切れています。確認用のメールを再送してください。" #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "アカウントをまだお持ちでなければ、 %(link)sこちらからユーザー登" "録%(end_link)s してください。" #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "パスキーでログイン" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "ログインコードを送信する" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "ログアウト" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "ログアウトしますか?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "メインのメールアドレス (%(email)s) は削除できません。" #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "確認メールを %(email)s へ送信しました。" #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "%(email)s を確認しました。" #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "メールアドレス %(email)s を削除しました。" #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "ユーザー %(name)s としてログインしました。" #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "ログアウトしました。" #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "ログインコードを %(recipient)s へ送信しました。" #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "パスワードを変更しました。" #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "パスワードを設定しました。" #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "確認コードを %(phone)s へ送信しました。" #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "あなたの電話番号 %(phone)s を照合しました。" #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "メインメールアドレスを設定しました。" #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "パスワード変更" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "パスワードをお忘れですか?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "パスワードをお忘れですか? パスワードを再設定するために、メールアドレスを入力" "してください。" #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "パスワードを再設定する" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "パスワードの再設定に問題がある場合はご連絡ください。" #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "確認のためのメールを送信しました。確認メールが届かないときは迷惑メールフォル" "ダをご確認ください。しばらくしてもメールが届かない場合はご連絡ください。" #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "不正なトークン" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "パスワード再設定のリンクが不正です。すでに使用された可能性があります。もう一" "度 パスワードの再設定をお試しください。" #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "パスワードを変更しました。" #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "パスワード設定" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "電話番号を変更する" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "現在の電話番号" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "あなたの電話番号はまだ確認されていません。" #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "電話番号を変更する" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "パスワードを入力してください。" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "パスワード不要のログイン用特別コードが記載されたメールをお送りします。" #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "コードを要求" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "そのほかのログイン方法" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "ユーザー登録" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "ユーザー登録" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "" "すでにアカウントをお持ちであれば、こちらから %(link)sログイン%(end_link)s し" "てください。" #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "パスキーでユーザー登録" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "パスキーでユーザー登録" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "ほかのオプション" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "ユーザー登録停止中" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "申し訳ありません、現在ユーザー登録を停止しています。" #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "注意" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "%(user_display)s さんとしてすでにログイン中です。" #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "注意:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "メールアドレスが設定されていません。通知を受け取ったり、パスワードを再設定し" "たりするためにはメールアドレスを登録する必要があります。" #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "メールアドレスを確認してください" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "確認のメールを送信しました。メールに記載されたリンクをクリックして、ユーザー" "登録を完了させてください。確認メールが届かないときは迷惑メールフォルダをご確" "認ください。しばらくしても確認のメールが届かない場合はお問い合わせください。" #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "このページにアクセスするためには、本人確認が必要です。\n" "そのために、登録されているメールアドレスがご自身のものであることを確認してい" "ただきます。 " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "確認のためのメールを送信しました。メールに記載されたリンクをクリックしてくだ" "さい。\n" "確認メールが届かないときは迷惑メールフォルダをご確認ください。\n" "しばらくしてもメールが届かない場合はお問い合わせください。" #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "注意: メールアドレスの変更をし" "ていただくことも可能です。" #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "メッセージ:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "メニュー:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "アカウント接続" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "多要素認証" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "セッション" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "認可する" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "" "%(client_name)s があなたの %(site_name)s アカウントへのアクセスを要求していま" "す。" #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "デバイスコードの入力" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "あなたのデバイスに表示されたコードを入力してください。" #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "続ける" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "デバイスの確認" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "あなたの %(client_name)s に表示されたデバイス認可用のコードをご確認ください。" #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "拒否する" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "デバイスが認可されました" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "あなたの %(client_name)s デバイスを認可しました。" #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "デバイスは拒否されました" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "あなたの %(client_name)s デバイスの認可は拒否されました。" #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "エラー" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "ログイン状態を保持する" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "あなたのアカウントは多要素認証で保護されています。認証コードを入力してくださ" "い:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "多要素認証用のリカバリーコードを新たに生成しました。" #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "新しいリカバリーコードを生成しました" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "認証アプリを有効化しました。" #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "認証アプリを有効化しました" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "認証アプリを無効化しました。" #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "認証アプリを無効化しました" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "新しいセキュリティキーを登録しました。" #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "セキュリティキーを追加しました" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "セキュリティキーを削除しました。" #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "セキュリティキーを削除しました" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "認証アプリ" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "認証アプリを使用した認証が有効です。" #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "認証アプリは有効ではありません。" #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "無効化する" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "有効化する" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "セキュリティキー" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "%(count)s 個のセキュリティキーを登録しました。" #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "登録されているセキュリティキーはありません。" #: templates/mfa/index.html:62 msgid "Manage" msgstr "管理" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "登録" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "リカバリーコード" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "利用可能なリカバリーコードは %(total_count)s 個中 %(unused_count)s 個です。" #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "リカバリーコードは設定されていません。" #: templates/mfa/index.html:96 msgid "View" msgstr "表示" #: templates/mfa/index.html:102 msgid "Download" msgstr "ダウンロード" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "生成する" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "新しいリカバリーコードを生成しました。" #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "セキュリティーキーを登録しました。" #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "セキュリティキーを削除しました。" #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "認証アプリのコードを入力:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "新しいリカバリーコードセットを生成しようとしています。" #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "この操作により、現在のコードが無効になります。" #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "本当によろしいですか?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "未使用のコード" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "コードをダウンロードする" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "新しいコードを生成する" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "認証アプリを有効化する" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "アカウントを多要素認証で保護するために、認証アプリで以下の QR コードをスキャ" "ンしてください。次に、アプリで生成された確認コードを以下に入力してください。" #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "認証アプリシークレット" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "このシークレットを保存し、後で認証アプリを再インストールする際に使用できま" "す。" #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "認証アプリを無効化する" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "認証アプリによる認証を無効化しようとしています。本当によろしいですか?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "このブラウザを信頼しますか?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "このブラウザを信頼すると、次回のログイン時に確認コードの入力が不要になりま" "す。" #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "信頼する (%(period)s)" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "信頼しない" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "セキュリティキーを追加" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "セキュリティキーを解除" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "セキュリティキーを解除しますか?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "使用方法" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "パスキー" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "セキュリティキー" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "このキーはパスキーかどうか示されていません。" #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "未指定" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "登録日: %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "最終使用日: %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "編集" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "セキュリティキーを編集" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "保存" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "パスキーを作成" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "あなたのアカウント用のパスキーを作成します。あとでキーの追加もできるため、" "キーに区別しやすい名前を付けてください。" #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "作成" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "この機能を利用するには JavaScript が必要です。" #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "外部アカウントによるログインに失敗しました" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "外部アカウントでのログイン時ににエラーが発生しました。" #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "以下の外部アカウントを使ってログインできます:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "あなたのカウントに結びつけられた外部アカウントはありません。" #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "外部アカウントを追加する" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "%(provider)s の外部アカウントをあなたのアカウントに接続しました。" #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "外部アカウントを接続しました" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "%(provider)s の外部アカウントへの接続を解除しました。" #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "外部アカウントとの接続を解除しました" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "%(provider)s と接続する" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "%(provider)s の新しい外部アカウントを接続しようとしています。" #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "%(provider)s でログイン" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "%(provider)s の外部アカウントを使用してログインしようとしています。" #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "ログインをキャンセルしました" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "既存の外部アカウントを使ったログインをキャンセルしました。\n" "やり直す場合はログインページにお進みください。" #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "外部アカウントを接続しました。" #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "外部アカウントとの接続を解除しました。" #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "%(provider_name)s アカウントを使って %(site_name)s にログインしようとしていま" "す。\n" "ユーザー登録のために、以下のフォームに記入してください。" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "外部アカウントを使用する" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "ほかのすべてのセッションをログアウトしました。" #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "開始日時" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP アドレス" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "ブラウザー" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "閲覧日時" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "現在の" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "ほかのセッションをログアウト" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "ユーザーセッション" #: usersessions/models.py:94 msgid "session key" msgstr "セッションキー" #, fuzzy #~ msgid "Account Connection" #~ msgstr "アカウント接続" #~ msgid "Use security key or device" #~ msgstr "セキュリティキー/デバイスを使用" #~ msgid "Add Security Key or Device" #~ msgstr "セキュリティキー/デバイスの登録" #~ msgid "Add key or device" #~ msgstr "キー/デバイスを登録" #~ msgid "Security Keys and Devices" #~ msgstr "セキュリティキー/デバイス" #~ msgid "You have not added any security keys/devices." #~ msgstr "登録されたセキュリティキー/デバイスはありません。" #~ msgid "Edit Security Key or Device" #~ msgstr "セキュリティキー/デバイスの編集" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "パスワードは {0} 文字以上の長さが必要です。" #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "このメールは、あなた(もしくは別の誰か)がパスワードの再設定を行おうとした" #~ "ために送られました。\n" #~ "パスワードの再設定を要求したのがあなたではない場合、このメールは無視してく" #~ "ださい。パスワードを再設定するためには、以下のリンクをクリックしてくださ" #~ "い。" #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "以下のメールアドレスがアカウントに登録されています:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "メールアドレスの確認" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "お持ちの外部アカウントでログインするか、%(site_name)sに ユーザー登録 してログインしてください。" #~ msgid "or" #~ msgstr "または" #~ msgid "change password" #~ msgstr "パスワード変更" #~ msgid "OpenID Sign In" #~ msgstr "OpenID ログイン" #~ msgid "This email address is already associated with another account." #~ msgstr "このメールアドレスは別のアカウントで使用されています。" ================================================ FILE: allauth/locale/ka/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Nikoloz Naskidashvili , 2021. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-10-20 04:54+0000\n" "Last-Translator: Temuri Doghonadze \n" "Language-Team: Georgian \n" "Language: ka\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.14-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "ეს ანგარიში ამჟამად გაუქმებულია." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "თქვენ არ შეგიძლიათ წაშალოთ თქვენი ძირითადი ელ. ფოსტის მისამართი." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "ეს ელ.ფოსტის მისამართი უკვე დაკავშირებულია ამ ანგარიშთან." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "თქვენს მიერ მითითებული ელ. ფოსტა ან პაროლი არასწორია." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "თქვენს მიერ მითითებული ტელეფონის ნომერი ან პაროლი არასწორია." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "მომხმარებელი ამ ელ. ფოსტით უკვე დარეგისტრირებულია." #: account/adapter.py:75 msgid "Please type your current password." msgstr "გთხოვთ აკრიფეთ მიმდინარე პაროლი." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "არასწორი კოდი." #: account/adapter.py:77 msgid "Incorrect password." msgstr "არასწორი პაროლი." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "არასწორი, ან ვადაამოწურული გასაღები." #: account/adapter.py:79 msgid "Invalid login." msgstr "არასწორი შესვლის მცდელობა." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "პაროლის აღდგენის კოდი არასწორია." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "თქვენ არ შეგიძლიათ დაამატოთ %d- ზე მეტი ელექტრონული ფოსტის მისამართი." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "მომხმარებელი ამ ტელეფონის ნომრით უკვე დარეგისტრირებულია." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "შესვლის ძალიან ბევრი წარუმატებელი მცდელობა. მოგვიანებით სცადეთ." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "" "ეს ელექტრონული ფოსტის მისამართი არ არის მიბმული რომელიმე მომხმარებლის " "ანგარიშზე." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "ეს ტელეფონის ნომერი არ არის მიბმული რომელიმე მომხმარებლის ანგარიშზე." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "თქვენი ძირითადი ელფოსტის მისამართი გადამოწმებული უნდა იყოს." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "" "ამ მომხმარებლის სახელის გამოყენება შეუძლებელია. გთხოვთ გამოიყენოთ სხვა." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "თქვენს მიერ მითითებული მომხმარებლის სახელი ან პაროლი არასწორია." #: account/adapter.py:98 msgid "Please select only one." msgstr "გთხოვთ აირჩიოთ მხოლოდ ერთი." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "ახალი მნიშვნელობა მიმდინარესგან განსხვავებული უნდა იყოს." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "მოითმინეთ, თქვენ ძალიან ბევრ მოთხოვნას აგზავნით." #: account/adapter.py:826 msgid "Use your password" msgstr "გამოიყენეთ თქვენი პაროლი" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "გამოიყენეთ ავთენტიფიკატორის აპლიკაცია ან კოდი" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "გამოიყენეთ უსაფრთხოების გასაღები" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} დაინიშნა, როგორც გადამოწმებული." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "{email}-ის გადამოწმებულად დანიშვნა ჩავარდა." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "არჩეული ელ. ფოსტის მისამართების გადამოწმებულად მონიშვნა" #: account/apps.py:11 msgid "Accounts" msgstr "მომხმარებლის ანგარიშები" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "ელ. ფოსტა" #: account/fields.py:19 msgid "Email address" msgstr "ელ. ფოსტა" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "შეიყვანეთ ტელეფონის ნომერი ქვეყნის კოდის ჩათვლით (მაგ. +1 აშშ-სთვის)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "ტელეფონი" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "თქვენ უნდა აკრიფოთ ერთი და იგივე პაროლი ყოველ ჯერზე." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "პაროლი" #: account/forms.py:67 msgid "Remember Me" msgstr "დამიმახსოვრე" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "მომხმარებლის სახელი" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "შესვლა" #: account/forms.py:115 msgid "Username, email or phone" msgstr "მომხმარებლის სახელი, ელ. ფოსტა ან ტელეფონი" #: account/forms.py:117 msgid "Username or email" msgstr "მომხმარებლის სახელი ან ელ. ფოსტა" #: account/forms.py:119 msgid "Username or phone" msgstr "მომხმარებლის სახელი ან ტელეფონი" #: account/forms.py:121 msgid "Email or phone" msgstr "ელ. ფოსტა ან ტელეფონი" #: account/forms.py:144 msgid "Forgot your password?" msgstr "დაგავიწყდათ პაროლი?" #: account/forms.py:287 msgid "Email (again)" msgstr "ელ. ფოსტა (გაამეორეთ)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "ელ. ფოსტის დადასტურება" #: account/forms.py:302 msgid "Email (optional)" msgstr "ელ. ფოსტა (არ არის აუცილებელი)" #: account/forms.py:314 msgid "Username (optional)" msgstr "მომხმარებლის სახელი (არასავალდებულო)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "თქვენ უნდა ჩაწეროთ ერთი და იგივე ელ. ფოსტა ყოველ ჯერზე." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "პაროლი (გაამეორეთ)" #: account/forms.py:591 msgid "Current Password" msgstr "მიმდინარე პაროლი" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "ახალი პაროლი" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "ახალი პაროლი (გაამეორეთ)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "კოდი" #: account/models.py:23 msgid "user" msgstr "მომხმარებელი" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "ელ. ფოსტა" #: account/models.py:31 msgid "verified" msgstr "დადასტურებული" #: account/models.py:32 msgid "primary" msgstr "პირველადი" #: account/models.py:38 msgid "email addresses" msgstr "ელ. ფოსტის ანგარიშები" #: account/models.py:142 msgid "created" msgstr "შექმნილი" #: account/models.py:143 msgid "sent" msgstr "გაგზავნილი" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "გასაღები" #: account/models.py:149 msgid "email confirmation" msgstr "ელ. ფოსტის დადასტურება" #: account/models.py:150 msgid "email confirmations" msgstr "ელ. ფოსტის დადასტურებები" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "თქვენი მომხმარებლის ID-ს ნახვა" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "თქვენი ელ. ფოსტის მისამართის ნახვა" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "თქვენი ძირითადი პროფილის ინფორმაციის ნახვა" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "ნებართვების მინიჭება" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "ბუნებრივი ნიშნები (*) დაშვებული არ არის, თუ 'URI ბუნებრივი ნიშნების დაშვება' " "ჩართული არ არის." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' შეიცავს ერთზე მეტ ბუნებრივ ნიშანს (*). თითო URI-ში მხოლოდ ერთი " "ბუნებრივი ნიშანი (*) არის დაშვებული." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "ბუნებრივი ნიშნები (*) მხოლოდ URI-ის ჰოსტის ნაწილში არის დაშვებული." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "ავტორიზაციის კოდი" #: idp/oidc/models.py:38 msgid "Device code" msgstr "მოწყობილობის კოდი" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "კლიენტის სერთიფიკატები" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "განახლების ტოკენი" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "კონფიდენციალური" #: idp/oidc/models.py:44 msgid "Public" msgstr "საჯარო" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "სფერო(ები), რომელთა მოთხოვნის უფლება აქვს კლიენტს. მიუთითეთ ერთი მნიშვნელობა " "თითო სტრიქონზე, მაგ.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "თუ კლიენტი არ მიუთითებს სფეროს, გამოიყენება ეს ნაგულისხმევი სფეროები. " "მიუთითეთ ერთი მნიშვნელობა თითო სტრიქონზე, მაგ.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "დაშვებული გრანტის ტიპების სია. მიუთითეთ ერთი მნიშვნელობა თითო სტრიქონზე, " "მაგ.: authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "ქროს-ორიჯინ მოთხოვნებისთვის დაშვებული წარმოშობის სია, თითო სტრიქონზე." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "ბუნებრივი ნიშნების (*) დაშვება გადამისამართების URI-ებსა და CORS " "წარმოშობებში. ჩართვისას, URI-ები შეიძლება შეიცავდეს ერთ ვარსკვლავს (*) " "ქვედომენების შესატყვისად." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "დაშვებული პასუხის ტიპების სია. მიუთითეთ ერთი მნიშვნელობა თითო სტრიქონზე, " "მაგ.: code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "კლიენტი" #: idp/oidc/models.py:116 msgid "clients" msgstr "კლიენტები" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "თქვენ არ შეგიძლიათ ელ. ფოსტის მისამართის დამატება ორფაქტორიანი " "ავთენტიფიკაციით დაცულ ანგარიშზე." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "თქვენ არ შეგიძლიათ ორფაქტორიანი ავთენტიფიკაციის გამორთვა." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "თქვენ არ შეგიძლიათ აღდგენის კოდების გენერირება ორფაქტორიანი ავთენტიფიკაციის " "ჩართვის გარეშე." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "თქვენ ვერ ჩართავთ ორფაქტორიან ავთენტიფიკაციას, სანამ არ დაადასტურებთ თქვენს " "ელ. ფოსტის მისამართს." #: mfa/adapter.py:141 msgid "Master key" msgstr "მთავარი გასაღები" #: mfa/adapter.py:143 msgid "Backup key" msgstr "სარეზერვო გასაღები" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "გასაღები №{number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "აღდგენის კოდები" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP ავთენტიფიკატორი" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "ავთენტიფიკატორის კოდი" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "უპაროლო" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "უპაროლო რეჟიმის ჩართვა საშუალებას გაძლევთ შეხვიდეთ მხოლოდ ამ გასაღების " "გამოყენებით, მაგრამ მოითხოვს დამატებით პირობებს, როგორიცაა ბიომეტრიკა ან PIN-" "კოდით დაცვა." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "ანგარიში ამ ელ. ფოსტის მისამართით უკვე არსებობს. გთხოვთ, ჯერ შეხვიდეთ იმ " "ანგარიშში, შემდეგ კი დააკავშიროთ თქვენი %s ანგარიში." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "არასწორი კოდი." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "თქვენს ანგარიშს არ აქვს პაროლი დაყენებული." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "თქვენს ანგარიშს არ აქვს დადასტურებული ელ. ფოსტა." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "თქენს უკანასკნელ მესამე პირების მიერ დამატებულ ანგარიშს ვერ გათიშავთ." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "მესამე მხარის ანგარიში უკვე მიბმულია სხვა ანგარიშთან." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "სოციალური ანგარიშები" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "პროვაიდერი" #: socialaccount/models.py:53 msgid "provider ID" msgstr "პროვაიდერის ID" #: socialaccount/models.py:57 msgid "name" msgstr "სახელი" #: socialaccount/models.py:59 msgid "client id" msgstr "კლიენტის id" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "აპლიკაციის ID ან მომხმარებლის კოდი" #: socialaccount/models.py:64 msgid "secret key" msgstr "საიდუმლო კოდი" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "" "API-ს საიდუმლო კოდი, კლიენტის საიდუმლო კოდი ან მომხმარებლის საიდუმლო კოდი" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "გასაღები" #: socialaccount/models.py:82 msgid "social application" msgstr "სოციალური აპლიკაცია" #: socialaccount/models.py:83 msgid "social applications" msgstr "სოციალური აპლიკაციები" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "ბოლო შესვლის თარიღი" #: socialaccount/models.py:121 msgid "date joined" msgstr "ანგარიშის შექმნის თარიღი" #: socialaccount/models.py:122 msgid "extra data" msgstr "სხვა მონაცემები" #: socialaccount/models.py:126 msgid "social account" msgstr "სოციალური ანგარიში" #: socialaccount/models.py:127 msgid "social accounts" msgstr "სოციალური ანგარიშები" #: socialaccount/models.py:161 msgid "token" msgstr "კოდი" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) ან access token (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "საიდუმლო კოდი" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) ან refresh token (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "ვადა გაუსვლის თარიღი" #: socialaccount/models.py:175 msgid "social application token" msgstr "სოციალური ანგარიშის კოდი" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "სოციალური ანგარიშების კოდი" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "პროფილის მონაცემები არასწორია" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "შესვლა" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "გაუქმება" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "არასწორი პასუხი მოთხოვნის მიღებისას \"%s\"-დან. მიღებულია პასუხი: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "არასწორი პასუხი მოთხოვნის წვდომის კოდის მიღებისას \"%s\"-დან." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "მოთხოვნის კოდი არ არის შენახული \"%s\" -სთვის." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "მოთხოვნის წვდომის კოდი არ არის შენახული \"%s\" -სთვის." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "\"%s\" - ზე კერძო რესურსებზე წვდომა არ არის." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "არასწორი პასუხი კოდის მიღებისას \"%s\"-დან." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "ანგარიში გაუქმებულია" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "ეს ანგარიში გაუქმებულია." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "ჩვენ გამოგიგზავნეთ კოდი %(recipient)s-ზე. კოდს მალე ვადა გაუვა, ამიტომ " "გთხოვთ შეიყვანოთ დროულად." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "დადასტურება" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "ახალი კოდის მოთხოვნა" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "წვდომის დადასტურება" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "გთხოვთ ხელახლა გაიაროთ ავთენტიფიკაცია თქვენი ანგარიშის დასაცავად." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "ალტერნატიული ვარიანტები" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "ელ. ფოსტის ვერიფიკაცია" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "შეიყვანეთ ელ. ფოსტის ვერიფიკაციის კოდი" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "გამოიყენეთ სხვა ელ. ფოსტის მისამართი" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "შესვლა" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "შეიყვანეთ შესვლის კოდი" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "პაროლის შეცვლა" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "შეიყვანეთ პაროლის აღდგენის კოდი" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "ტელეფონის ვერიფიკაცია" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "შეიყვანეთ ტელეფონის ვერიფიკაციის კოდი" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "გამოიყენეთ სხვა ტელეფონის ნომერი" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "ელ. ფოსტის მისამართები" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "შემდეგი ელ.ფოსტის მისამართები ასოცირდება თქვენს ანგარიშთან:" #: templates/account/email.html:25 msgid "Verified" msgstr "დადასტურებული" #: templates/account/email.html:29 msgid "Unverified" msgstr "არ არის დადასტურებელი" #: templates/account/email.html:34 msgid "Primary" msgstr "პირველადი" #: templates/account/email.html:44 msgid "Make Primary" msgstr "გახადე პირველადი" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "ვერიფიკაციის თავიდან გაგზავნა" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "წაშლა" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "ელ. ფოსტის მისამართის დამატება" #: templates/account/email.html:70 msgid "Add Email" msgstr "ელ. ფოსტის დამატება" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "ნამდვილად გსურთ წაშალოთ არჩეული ელ.ფოსტის მისამართი?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "თქვენ იღებთ ამ ელ. წერილს, რადგან თქვენ ან სხვა ვიღაცამ სცადა რეგისტრაცია\n" "შემდეგი ელ. ფოსტის მისამართით:\n" "\n" "%(email)s\n" "\n" "თუმცა, ამ ელ. ფოსტის მისამართით ანგარიში უკვე არსებობს. თუ\n" "დაგავიწყდათ, გთხოვთ გამოიყენოთ პაროლის აღდგენის პროცედურა\n" "თქვენი ანგარიშის აღსადგენად:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "ანგარიში უკვე არსებობს" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "მოგესალმებით %(site_name)s!-დან!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "მადლობა რომ იყენებ %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "თქვენ იღებთ ამ ელ. წერილს, რადგან თქვენს ანგარიშში შემდეგი ცვლილება " "განხორციელდა:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "თუ ეს ცვლილება არ გეცნობათ, გთხოვთ დაუყოვნებლივ მიიღოთ შესაბამისი " "უსაფრთხოების ზომები. ცვლილება თქვენს ანგარიშზე განხორციელდა:\n" "\n" "- IP მისამართი: %(ip)s\n" "- ბრაუზერი: %(user_agent)s\n" "- თარიღი: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "თქვენი ელ. ფოსტა შეიცვალა %(from_email)s-დან %(to_email)s-ზე." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "ელ. ფოსტა შეიცვალა" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "თქვენი ელ. ფოსტა დადასტურებულია." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "ელ. ფოსტის დადასტურება" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "თქვენ იღებთ ამ ელ. წერილს რადგან მომხმარებემა - %(user_display)s მიუთითა " "თქვენი ელ.ფოსტის ანგარიში რომ დარეგისტრირდეს შემდეგ საიტზე - %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "თქვენი ელ. ფოსტის ვერიფიკაციის კოდი მოცემულია ქვემოთ. გთხოვთ შეიყვანოთ ის " "თქვენს ღია ბრაუზერის ფანჯარაში." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "იმის დასადასტურებლად, რომ ეს სწორია, გადადით %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "გთხოვთ დაადასტუროთ თქვენი ელ. ფოსტა" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "ელ. ფოსტის მისამართი %(deleted_email)s წაშლილია თქვენი ანგარიშიდან." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "ელ. ფოსტა წაშლილია" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "თქვენი შესვლის კოდი მოცემულია ქვემოთ. გთხოვთ შეიყვანოთ ის თქვენს ღია " "ბრაუზერის ფანჯარაში." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "თქვენ შეგიძლიათ უსაფრთხოდ უგულებელყოთ ეს წერილი, თუ ეს მოქმედება თქვენი " "ინიცირებული არ იყო." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "შესვლის კოდი" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "თქვენი პაროლი შეიცვალა." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "პაროლი შეიცვალა" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "თქვენი პაროლის აღდგენის კოდი მოცემულია ქვემოთ. გთხოვთ შეიყვანოთ ის თქვენს " "ღია ბრაუზერის ფანჯარაში." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "პაროლის აღდგენის კოდი" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "თქვენ იღებთ ამ ელ. წერილს, რადგან თქვენ ან სხვა ვიღაცამ მოითხოვა პაროლის " "აღდგენა თქვენს ანგარიშზე.\n" "თქვენ შეგიძლიათ უბრალოდ უგულებელყოთ ეს ელ. წერილი, თუ ეს თქვენი მოთხოვნა არ " "იყო. დააჭირეთ ქვემოთ მოცემულ ბმულს პაროლის აღსადგენად." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "თუ თქვენ დაგავიწყდათ, თქვენი მომხმარებლის სახელია: %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "პაროლის აღდგენის ელ. წერილი" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "თქვენი პაროლი ჩამოიყარა." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "თქვენი პაროლი დაყენებულია." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "პაროლი დაყენებულია" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "თქვენ იღებთ ამ ელ. წერილს, რადგან თქვენ ან სხვა ვიღაცამ სცადა ანგარიშზე " "წვდომა ელ. ფოსტით %(email)s. თუმცა, ჩვენს მონაცემთა ბაზაში ასეთი ანგარიშის " "ჩანაწერი არ მოიძებნა." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "თუ ეს თქვენ იყავით, შეგიძლიათ დარეგისტრირდეთ ქვემოთ მოცემული ბმულის " "გამოყენებით." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "უცნობი ანგარიში" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "ელ. ფოსტის მისამართი" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "მიმდინარე ელ. ფოსტა" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "იცვლება" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "თქვენი ელფოსტის მისამართი ჯერ კიდევ ითხოვს გადამოწმებას." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "ცვლილების გაუქმება" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "შეცვლა" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "ელ. ფოსტის შეცვლა" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "დაადასტურეთ ელ. ფოსტა" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "გთხოვთ დაადასტუროთ, რომ %(email)s არის ელ. " "ფოსტის მისამართი მომხმარებლისთვის %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "ელფოსტის %(email)s დადასტურება შეუძლებელია, რადგან ის უკვე დადასტურებულია " "სხვა ანგარიშისთვის." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "ეს ელ. ფოსტის დადასტურების ბმული ვადაგასულია ან არასწორია. გთხოვთ გააკეთოთ ახალი ელ. ფოსტის დადასტურების მოთხოვნა." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "თუ არ გაქვთ შექმნილი ანგარიში, მაშინ გთხოვთ " "%(link)sდარეგისტრირდით%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "შესვლა გასაღებით (passkey)" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "გამომიგზავნეთ შესვლის კოდი" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "გასვლა" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "დარწმუნებული ხარ, რომ გინდა გასვლა?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "შენ არ შეგიძლია წაშალო შენი პირველადი ელ. ფოსტა (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "ელ. ფოსტის დადასტურების წერილი გაგზავნილია %(email)s მისამართით." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "თქვენ დაადასტურეთ %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "ელ. ფოსტა %(email)s წაშლილია." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "თქვენ წარმატებით შეხვედით %(name)s-ად." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "თქვენ წარმატებით გახვედით თქვენი ანგარიშიდან." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "შესვლის კოდი გაგზავნილია %(recipient)s-ზე." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "პაროლი წარმატებით შეცვლილია." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "პაროლი წარმატებით დაყენებულია." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "ვერიფიკაციის კოდი გაგზავნილია %(phone)s-ზე." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "თქვენ დაადასტურეთ ტელეფონის ნომერი %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "ძირითადი ელფოსტის მისამართი დაყენებულია." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "პაროლის შეცვლა" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "დაგავიწყდა პაროლი?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "დაგავიწყდათ პაროლი? შეიყვანეთ თქვენი ელ. ფოსტის მისამართი ქვემოთ და ჩვენ " "გამოგიგზავნით ელ. წერილს პაროლის აღსადგენად." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "ჩემი პაროლის შეცვლა" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "გთხოვთ დაგვიკავშირდით თუ გაქვთ პრობლემა პაროლის შეცვლასთან." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "ჩვენ ელფოსტა გამოგიგზავნეთ. თუ არ მიგიღიათ, შეამოწმეთ თქვენი სპამის " "საქაღალდე. დაგვიკავშირდით, თუ ელფოსტას რამდენიმე წუთში არ მიიღებთ." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "შეცდომა, ცუდი კოდი" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "პაროლის აღდგენის ლინკი არასწორია, ალბათ იმის გამო რომ უკვე გამოყენებული " "იქნა. გთხოვთ მოითხოვეთ პაროლის შეცვლა " "თავიდან." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "თქვენი პაროლი ახლა შეცვლილია." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "პაროლის დაყენება" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "ტელეფონის შეცვლა" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "მიმდინარე ტელეფონი" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "თქვენი ტელეფონის ნომერი ჯერ კიდევ ითხოვს ვერიფიკაციას." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "ტელეფონის შეცვლა" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "შეიყვანეთ თქვენი პაროლი:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "თქვენ მიიღებთ სპეციალურ კოდს უპაროლო შესვლისთვის." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "კოდის მოთხოვნა" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "სხვა შესვლის ვარიანტები" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "რეგისტრაცია" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "რეგისტრაცია" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "უკვე გაქვს ანგარიში? %(link)sშესვლა%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "რეგისტრაცია გასაღებით (passkey)" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "რეგისტრაცია გასაღებით (Passkey)" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "სხვა ვარიანტები" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "რეგისტრაცია დროებით გაუქმებულია" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "ბოდიში, რეგისტრაცია დროებით გაუქმებულია." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "შენიშვნა" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "თქვენ უკვე შესული ხართ როგორც %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "გაფრთხილება:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "თქვენ ამჟამად არ გაქვთ ელ. ფოსტის მისამართი დაყენებული. რეკომენდირებულია " "დაამატოთ ელ. ფოსტის მისამართი, რათა მიიღოთ შეტყობინებები, აღადგინოთ პაროლი " "და ა.შ." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "დაადასტურეთ თქვენი ელ. ფოსტა" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "ჩვენ გამოგიგზავნეთ ელ. წერილი ვერიფიკაციისთვის. მიჰყევით მასში მოცემულ " "ბმულს, რომ დაასრულოთ რეგისტრაცია. თუ ვერ ხედავთ ვერიფიკაციის ელ. წერილს " "თქვენს ძირითად საფოსტო ყუთში, შეამოწმეთ სპამის საქაღალდე. გთხოვთ " "დაგვიკავშირდით, თუ ვერიფიკაციის ელ. წერილს რამდენიმე წუთში არ მიიღებთ." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "საიტის ეს ნაწილი ითხოვს იმის დადასტურებას, რომ\n" "თქვენ ხართ ის, ვინც აცხადებთ, რომ ხართ. ამისთვის ჩვენ ვითხოვთ\n" "თქვენი ელ. ფოსტის მისამართის საკუთრების დადასტურებას. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "ჩვენ გამოგიგზავნეთ ელ. წერილი\n" "ვერიფიკაციისთვის. გთხოვთ დააჭიროთ მასში მოცემულ ბმულს. თუ ვერ ხედავთ " "ვერიფიკაციის ელ. წერილს თქვენს ძირითად საფოსტო ყუთში, შეამოწმეთ სპამის " "საქაღალდე. წინააღმდეგ შემთხვევაში\n" "დაგვიკავშირდით, თუ მას რამდენიმე წუთში არ მიიღებთ." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "შენიშვნა: თქვენ ჯერ კიდევ შეგიძლიათ შეცვალოთ ელ. ფოსტის მისამართი." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "შეტყობინებები:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "მენიუ:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "ანგარიშის კავშირები" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "ორფაქტორიანი ავთენტიფიკაცია" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "სესიები" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "ავტორიზაცია" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s სურს წვდომა თქვენს %(site_name)s ანგარიშზე." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "შეიყვანეთ მოწყობილობის კოდი" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "შეიყვანეთ თქვენს მოწყობილობაზე ნაჩვენები კოდი." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "გაგრძელება" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "მოწყობილობის დადასტურება" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "გთხოვთ დაადასტუროთ %(client_name)s-ზე ნაჩვენები კოდი ამ მოწყობილობის " "ავტორიზაციისთვის." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "უარყოფა" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "მოწყობილობა ავტორიზებულია" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "თქვენ წარმატებით გაიარეთ %(client_name)s მოწყობილობის ავტორიზაცია." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "მოწყობილობა უარყოფილია" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "თქვენი %(client_name)s მოწყობილობის ავტორიზაცია უარყოფილია." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "შეცდომა" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "დარჩი შესული" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "თქვენი ანგარიში დაცულია ორფაქტორიანი ავთენტიფიკაციით. გთხოვთ შეიყვანოთ " "ავთენტიფიკატორის კოდი:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "ორფაქტორიანი ავთენტიფიკაციის აღდგენის კოდების ახალი ნაკრები გენერირებულია." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "ახალი აღდგენის კოდები გენერირებულია" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "ავთენტიფიკატორის აპლიკაცია გააქტიურებულია." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "ავთენტიფიკატორის აპლიკაცია გააქტიურებულია" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "ავთენტიფიკატორის აპლიკაცია გამორთულია." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "ავთენტიფიკატორის აპლიკაცია გამორთულია" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "ახალი უსაფრთხოების გასაღები დამატებულია." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "უსაფრთხოების გასაღები დამატებულია" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "უსაფრთხოების გასაღები წაშლილია." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "უსაფრთხოების გასაღები წაშლილია" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "ავთენტიფიკატორის აპლიკაცია" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "ავთენტიფიკატორის აპლიკაციით ავთენტიფიკაცია აქტიურია." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "ავთენტიფიკატორის აპლიკაცია არ არის აქტიური." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "გამორთვა" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "გააქტიურება" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "უსაფრთხოების გასაღებები" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "თქვენ დაამატეთ %(count)s უსაფრთხოების გასაღები." msgstr[1] "თქვენ დაამატეთ %(count)s უსაფრთხოების გასაღები." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "უსაფრთხოების გასაღებები არ არის დამატებული." #: templates/mfa/index.html:62 msgid "Manage" msgstr "მართვა" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "დამატება" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "აღდგენის კოდები" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "ხელმისაწვდომია %(unused_count)s აღდგენის კოდი %(total_count)s-დან." msgstr[1] "ხელმისაწვდომია %(unused_count)s აღდგენის კოდი %(total_count)s-დან." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "აღდგენის კოდები არ არის დაყენებული." #: templates/mfa/index.html:96 msgid "View" msgstr "ნახვა" #: templates/mfa/index.html:102 msgid "Download" msgstr "ჩამოტვირთვა" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "გენერირება" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "აღდგენის კოდების ახალი ნაკრები გენერირებულია." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "უსაფრთხოების გასაღები დამატებულია." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "უსაფრთხოების გასაღები წაშლილია." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "შეიყვანეთ ავთენტიკაციის კოდი:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "" "თქვენ აპირებთ თქვენი ანგარიშისთვის აღდგენის კოდების ახალი ნაკრების " "გენერირებას." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "ეს მოქმედება გააუქმებს თქვენს არსებულ კოდებს." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "დარწმუნებული ხართ?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "გამოუყენებელი კოდები" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "კოდების ჩამოტვირთვა" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "ახალი კოდების გენერირება" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "ავთენტიფიკატორის აპლიკაციის გააქტიურება" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "თქვენი ანგარიშის ორფაქტორიანი ავთენტიფიკაციით დასაცავად, დაასკანირეთ ქვემოთ " "მოცემული QR კოდი თქვენი ავთენტიფიკატორის აპლიკაციით. შემდეგ, შეიყვანეთ " "აპლიკაციის მიერ გენერირებული ვერიფიკაციის კოდი ქვემოთ." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "ავთენტიფიკატორის საიდუმლო კოდი" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "თქვენ შეგიძლიათ შეინახოთ ეს საიდუმლო კოდი და გამოიყენოთ ის ავთენტიფიკატორის " "აპლიკაციის მოგვიანებით ხელახლა ინსტალაციისთვის." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "ავთენტიფიკატორის აპლიკაციის გამორთვა" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "თქვენ აპირებთ ავთენტიფიკატორის აპლიკაციაზე დაფუძნებული ავთენტიფიკაციის " "გამორთვას. დარწმუნებული ხართ?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "ენდობით ამ ბრაუზერს?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "თუ ამ ბრაუზერს ენდობით, შემდეგ შესვლისას ვერიფიკაციის კოდი აღარ მოგეთხოვებათ." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "ენდობა %(period)s-ის განმავლობაში" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "არ ვენდობი" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "უსაფრთხოების გასაღების დამატება" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "უსაფრთხოების გასაღების წაშლა" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "დარწმუნებული ხართ, რომ გინდათ ამ უსაფრთხოების გასაღების წაშლა?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "გამოყენება" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "გასაღები (Passkey)" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "უსაფრთხოების გასაღები" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "ეს გასაღები არ მიუთითებს, არის თუ არა ის passkey." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "დაუზუსტებელი" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "დამატებულია %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "ბოლოს გამოყენებული %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "რედაქტირება" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "უსაფრთხოების გასაღების რედაქტირება" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "შენახვა" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "გასაღების (Passkey) შექმნა" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "თქვენ აპირებთ თქვენი ანგარიშისთვის გასაღების (passkey) შექმნას. რადგან " "მოგვიანებით დამატებითი გასაღებების დამატებაც შეგიძლიათ, შეგიძლიათ აღწერითი " "სახელი გამოიყენოთ მათ გასარჩევად." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "შექმნა" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "ეს ფუნქცია მოითხოვს JavaScript-ს." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "მესამე მხარის ანგარიშით შესვლის შეცდომა" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "დაფიქსირდა შეცდომა მესამე მხარის ანგარიშით შესვლის მცდელობისას." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "თქვენ შეგიძლიათ შეხვიდეთ თქვენს ანგარიშში რომელიმე შემდეგი მესამე მხარის " "ანგარიშის გამოყენებით:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "" "თქვენ ამჟამად არ გაქვთ ამ ანგარიშთან დაკავშირებული მესამე მხარის ანგარიშები." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "მესამე მხარის ანგარიშის დამატება" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "" "%(provider)s-ის მესამე მხარის ანგარიში დაკავშირებულია თქვენს ანგარიშთან." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "მესამე მხარის ანგარიში დაკავშირებულია" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "%(provider)s-ის მესამე მხარის ანგარიში გათიშულია თქვენი ანგარიშიდან." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "მესამე მხარის ანგარიში გათიშულია" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "%(provider)s ანგარიშის დაკავშირება" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "" "თქვენ აპირებთ %(provider)s-ის ახალი მესამე მხარის ანგარიშის დაკავშირებას." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "შესვლა %(provider)s ანგარიშით" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "თქვენ აპირებთ შესვლას %(provider)s-ის მესამე მხარის ანგარიშით." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "შესვლა გაუქმებულია" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "თქვენ გადაწყვიტეთ გააუქმოთ შესვლა ჩვენს საიტზე თქვენი ერთ-ერთი არსებული " "ანგარიშის გამოყენებით. თუ ეს შეცდომა იყო, გთხოვთ ხელახლა შედით." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "მესამე მხარის ანგარიში დაკავშირებულია." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "მესამე მხარის ანგარიში გათიშულია." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "თქვენ აპირებთ გამოიყენოთ თქვენი %(provider_name)s ანგარიში შესასვლელად\n" "%(site_name)s-ზე. როგორც საბოლოო ნაბიჯი, გთხოვთ შეავსეთ ეს ფორმა:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "ან გამოიყენეთ მესამე მხარე" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "ყველა სხვა სესიიდან გასვლა შესრულებულია." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "დაწყებულია" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP მისამართი" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "ბრაუზერი" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "ბოლოს ნანახი" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "მიმდინარე" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "სხვა სესიებიდან გასვლა" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "მომხმარებლის სესიები" #: usersessions/models.py:94 msgid "session key" msgstr "სესიის გასაღები" #, fuzzy #~ msgid "Account Connection" #~ msgstr "ანგარიშის კავშირები" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "პაროლი უნდა შეიცავდეს მინიმუმ {0} სიმბოლოს." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "თქვენ იღებთ ამ ელ. წერილს რადგან თქვენ ან სხვა ვიღაცამ მოითხოვა პაროლის " #~ "შეცვლა თქვენს ანგარიშზე.\n" #~ "თქვენ შეგიძლიათ უბრალოდ დააიგნოროთ ეს ელ. წერილი თუ ეს თქვენი მოთხოვნა არ " #~ "იყო. დააჭირეთ ქვემოთ მოცემულ ლინკს პაროლის აღსადგენად." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "შემდეგი ელ.ფოსტის მისამართები ასოცირდება თქვენს ანგარიშთან:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "დაადასტურეთ ელ. ფოსტა" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "გთხოვთ შედით რომელიმე\n" #~ "სოციალური ანგარიშით. ან, %(link)sდარეგისტრირდით %(end_link)s\n" #~ "ან შედით %(site_name)s საიტზე" #~ msgid "or" #~ msgstr "ან" #~ msgid "change password" #~ msgstr "პაროლის შეცვლა" #~ msgid "OpenID Sign In" #~ msgstr "OpenID-ით შესვლა" #~ msgid "This email address is already associated with another account." #~ msgstr "ეს ელ.ფოსტის მისამართი უკვე დაკავშირებულია სხვა ანგარიშთან." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "ჩვენ გამოგიგზავნეთ ელ. წერილი. გთხოვთ დაგვიკავშირდით თუ არ მიიღებთ " #~ "რამდენიმე წუთში." ================================================ FILE: allauth/locale/ko/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-09-17 16:01+0000\n" "Last-Translator: Hyunjoon \n" "Language-Team: Korean \n" "Language: ko\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 5.14-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "해당 계정은 현재 비활성화 상태입니다." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "주 이메일은 제거할 수 없습니다." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "해당 이메일은 이미 이 계정에 등록되어 있습니다." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "이메일 또는 비밀번호가 올바르지 않습니다." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "전화번호 또는 비밀번호가 올바르지 않습니다." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "해당 이메일은 이미 사용되고 있습니다." #: account/adapter.py:75 msgid "Please type your current password." msgstr "현재 비밀번호를 입력하세요." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "올바르지 않은 코드." #: account/adapter.py:77 msgid "Incorrect password." msgstr "올바르지 않은 비밀번호." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "올비르지 않거나 만료된 키." #: account/adapter.py:79 msgid "Invalid login." msgstr "유효하지 않은 로그인입니다." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "비밀번호 초기화 토큰이 올바르지 않습니다." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "이메일 주소는 %d개 이상 추가할 수 없습니다." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "해당 전화번호는 이미 사용되고 있습니다." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "너무 많은 로그인 실패가 감지되었습니다. 잠시 후에 다시 시도하세요." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "해당 이메일 주소와 연결된 사용자 계정이 없습니다." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "해당 전화번호와 연결된 사용자 계정이 없습니다." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "주 이메일은 인증이 필요합니다." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "해당 아이디는 이미 사용중입니다. 다른 사용자명을 이용해 주세요." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "아이디 또는 비밀번호가 올바르지 않습니다." #: account/adapter.py:98 msgid "Please select only one." msgstr "하나만 선택해 주세요." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "새 값은 현재 값과 달라야 합니다." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "잠시만 기다려 주세요. 요청을 너무 많이 보내고 있습니다." #: account/adapter.py:826 msgid "Use your password" msgstr "비밀번호를 사용하세요" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "인증 앱 또는 코드를 사용하세요" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "보안키를 사용하세요" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email}을(를) 인증됨으로 표시했습니다." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "{email}을(를) 인증 완료로 표시하지 못했습니다." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "선택된 이메일 주소를 인증됨으로 표시" #: account/apps.py:11 msgid "Accounts" msgstr "계정" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "이메일" #: account/fields.py:19 msgid "Email address" msgstr "이메일 주소" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "국가 코드를 포함한 전화번호를 입력하세요 (예: 미국의 경우 +1)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "전화번호" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "동일한 비밀번호를 입력해야 합니다." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "비밀번호" #: account/forms.py:67 msgid "Remember Me" msgstr "아이디 저장" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "아이디" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "로그인" #: account/forms.py:115 msgid "Username, email or phone" msgstr "아이디, 이메일 또는 전화번호" #: account/forms.py:117 msgid "Username or email" msgstr "아이디 또는 이메일" #: account/forms.py:119 msgid "Username or phone" msgstr "아이디 또는 전화번호" #: account/forms.py:121 msgid "Email or phone" msgstr "이메일 또는 전화번호" #: account/forms.py:144 msgid "Forgot your password?" msgstr "비밀번호를 잊으셨나요?" #: account/forms.py:287 msgid "Email (again)" msgstr "이메일 (확인)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "이메일 주소 확인" #: account/forms.py:302 msgid "Email (optional)" msgstr "이메일 (선택사항)" #: account/forms.py:314 msgid "Username (optional)" msgstr "아이디 (선택사항)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "동일한 이메일을 입력해야 합니다." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "비밀번호 (확인)" #: account/forms.py:591 msgid "Current Password" msgstr "현재 비밀번호" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "새 비밀번호" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "새 비밀번호 (확인)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "코드" #: account/models.py:23 msgid "user" msgstr "사용자" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "이메일 주소" #: account/models.py:31 msgid "verified" msgstr "인증완료" #: account/models.py:32 msgid "primary" msgstr "주" #: account/models.py:38 msgid "email addresses" msgstr "이메일 주소" #: account/models.py:142 msgid "created" msgstr "생성됨" #: account/models.py:143 msgid "sent" msgstr "전송됨" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "키" #: account/models.py:149 msgid "email confirmation" msgstr "이메일 확인" #: account/models.py:150 msgid "email confirmations" msgstr "이메일 확인" #: headless/apps.py:7 msgid "Headless" msgstr "헤드리스" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "사용자 ID 보기" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "이메일 주소 보기" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "기본 프로필 정보 보기" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "권한 부여" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "'URI 와일드카드 허용'이 활성화되지 않은 경우 와일드카드를 사용할 수 없습니다." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}'에 와일드카드(*)가 두 개 이상 포함되어 있습니다. URI당 와일드카드는 " "하나만 허용됩니다." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "와일드카드는 URI의 호스트명 부분에서만 허용됩니다." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "인가 코드" #: idp/oidc/models.py:38 msgid "Device code" msgstr "디바이스 코드" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "클라이언트 자격 증명" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "리프레시 토큰" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "기밀" #: idp/oidc/models.py:44 msgid "Public" msgstr "공개" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "클라이언트가 요청할 수 있는 범위(scope)입니다. 한 줄에 하나씩 입력하세요. " "예: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "클라이언트가 범위를 지정하지 않은 경우 이 기본 범위가 사용됩니다. 한 줄에 하" "나씩 입력하세요. 예: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "허용된 인가 유형 목록입니다. 한 줄에 하나씩 입력하세요. 예: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "교차 출처 요청에 허용된 출처 목록입니다. 한 줄에 하나씩 입력하세요." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "리디렉트 URI 및 CORS 출처에서 와일드카드(*)를 허용합니다. 활성화하면 URI에 단" "일 별표를 포함하여 하위 도메인을 매칭할 수 있습니다." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "허용된 응답 유형 목록입니다. 한 줄에 하나씩 입력하세요. 예: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "클라이언트" #: idp/oidc/models.py:116 msgid "clients" msgstr "클라이언트 목록" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "2단계 인증으로 보호되는 계정에 이메일 주소를 추가할 수 없습니다." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "2단계 인증을 비활성화 할 수 없습니다." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "2단계 인증을 활성화하지 않으면 복구 코드를 생성할 수 없습니다." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "이메일 주소를 인증하기 전까지는 2단계 인증을 활성화할 수 없습니다." #: mfa/adapter.py:141 msgid "Master key" msgstr "마스터 키" #: mfa/adapter.py:143 msgid "Backup key" msgstr "벡업 키" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "키 번호. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "복구 코드" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP 인증" #: mfa/models.py:26 msgid "WebAuthn" msgstr "웹인증" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "인증 코드" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "비밀번호 없음" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "비밀번호 없는 작업을 활성화하면 이 키만 사용하여 로그인할 수 있지만, 생체 인" "식이나 PIN 보호와 같은 추가 요구 사항이 적용됩니다." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "해당 이메일을 사용중인 계정이 이미 존재합니다. 해당 계정으로 로그인 후에 %s " "계정으로 연결하세요." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "유효하지 않은 토큰." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "당신의 계정에 비밀번호가 설정되어있지 않습니다." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "당신의 계정에는 인증된 이메일이 없습니다." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "마지막 남은 소셜 계정은 연결해제 할수 없습니다." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "해당 소셜 계정이 이미 다른 계정에 연결되어 있습니다." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "소셜 계정" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "제공자" #: socialaccount/models.py:53 msgid "provider ID" msgstr "제공자 ID" #: socialaccount/models.py:57 msgid "name" msgstr "이름" #: socialaccount/models.py:59 msgid "client id" msgstr "클라이언트 아이디" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "앱 아이디 또는 컨슈머 아이디" #: socialaccount/models.py:64 msgid "secret key" msgstr "비밀 키" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API 비밀 키, 클라이언트 비밀 키, 또는 컨슈머 비밀 키" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "키" #: socialaccount/models.py:82 msgid "social application" msgstr "소셜 애플리케이션" #: socialaccount/models.py:83 msgid "social applications" msgstr "소셜 애플리케이션" #: socialaccount/models.py:118 msgid "uid" msgstr "사용자 식별" #: socialaccount/models.py:120 msgid "last login" msgstr "최종 로그인" #: socialaccount/models.py:121 msgid "date joined" msgstr "가입 날짜" #: socialaccount/models.py:122 msgid "extra data" msgstr "추가 정보" #: socialaccount/models.py:126 msgid "social account" msgstr "소셜 계정" #: socialaccount/models.py:127 msgid "social accounts" msgstr "소셜 계정" #: socialaccount/models.py:161 msgid "token" msgstr "토큰" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) 또는 access token (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "시크릿 토큰" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) 또는 refresh token (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "만료일" #: socialaccount/models.py:175 msgid "social application token" msgstr "소셜 애플리케이션 토큰" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "소셜 애플리케이션 토큰" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "올바르지 않은 프로필 데이터" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "로그인" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "취소" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "\"%s\".로 부터 요청 토큰을 받는 도중 잘못된 응답을 받았습니다. 응답은 %s였습" "니다." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "\"%s\".로 부터 access 토큰을 받는 도중 잘못된 응답을 받았습니다." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "\"%s\".을(를) 위한 request 토큰이 없습니다." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "\"%s\".을(를) 위한 access 토큰이 없습니다." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "\"%s\".에 접근하기 위한 권한이 없습니다." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "\"%s\".로 부터 request 토큰을 받는 도중 잘못된 응답을 받았습니다." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "계정 비활성" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "해당 계정은 비활성화된 상태입니다." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "%(recipient)s(으)로 코드를 보냈습니다. 코드가 곧 만료되므로 즉시 입력하세요." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "확인" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "새 코드 요청" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "접근 확인" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "계정을 보호하려면 다시 인증하세요." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "대체 옵션" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "이메일 인증" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "이메일 인증 코드 입력" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "다른 이메일 주소 사용" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "로그인" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "로그인 코드 입력" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "비밀번호 초기화" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "비밀번호 초기화 코드 입력" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "전화번호 인증" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "전화번호 인증 코드 입력" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "다른 전화번호 사용" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "이메일 계정" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "다음 이메일 주소들이 당신의 계정에 등록되어 있습니다." #: templates/account/email.html:25 msgid "Verified" msgstr "인증완료" #: templates/account/email.html:29 msgid "Unverified" msgstr "인증대기" #: templates/account/email.html:34 msgid "Primary" msgstr "주" #: templates/account/email.html:44 msgid "Make Primary" msgstr "주 이메일로 지정" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "인증 재전송" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "제거" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "이메일 주소 추가" #: templates/account/email.html:70 msgid "Add Email" msgstr "이메일 추가" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "정말로 선택하신 이메일을 제거하시겠습니까?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "귀하 또는 다른 사람이 다음 이메일 주소를 사용하여 계정을 등록하려고 시도했기" "에 이 이메일을 받으셨습니다:\n" "\n" "%(email)s\n" "\n" "하지만 이 이메일 주소를 사용하는 계정이 이미 존재합니다. 혹시 이 사실을 잊으" "셨다면, 비밀번호 찾기 절차를 통해 계정을 복구해 주세요:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "계정이 이미 존재합니다" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "안녕하세요 %(site_name)s입니다!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "%(site_name)s 서비스를 이용해 주셔서 감사합니다!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "귀하의 계정에 다음과 같은 변경이 이루어졌기 때문에 이 메일을 받으셨습니다:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "이 변경 사항을 인지하지 못하셨다면 즉시 적절한 보안 조치를 취해주시기 바랍니" "다. 귀하의 계정 변경 출처는 다음과 같습니다:\n" "\n" "- IP 주소: %(ip)s\n" "- 브라우저: %(user_agent)s\n" "- 날짜: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "귀하의 이메일이 %(from_email)s에서 %(to_email)s(으)로 변경되었습니다." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "이메일 변경됨" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "이메일이 확인 되었습니다." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "이메일 확인" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "%(user_display)s 님이 %(site_domain)s에서 계정을 등록하기 위해 귀하의 이메일 " "주소를 제공하였기 때문에 이 이메일을 받으셨습니다." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "이메일 인증 코드가 아래에 나와 있습니다. 열린 브라우저 창에 입력해 주세요." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "이메일 주소를 확인하기 위해, %(activate_url)s을 클릭하세요" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "이메일 주소를 확인하세요." #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "이메일 주소 %(deleted_email)s이(가) 귀하의 계정에서 삭제되었습니다." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "이메일 제거됨" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "귀하의 로그인 코드는 아래에 나와 있습니다. 열린 브라우저 창에 입력해 주시기 " "바랍니다." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "이 메일은 귀하가 이 작업을 시작하지 않았다면 안전하게 무시하셔도 됩니다." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "로그인 코드" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "비밀번호가 변경되었습니다." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "비밀번호 변경됨" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "비밀번호 초기화 코드가 아래에 나와 있습니다. 열린 브라우저 창에 입력해 주세" "요." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "비밀번호 초기화 코드" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "회원님의 계정에 대한 암호 변경 요청이 접수되었습니다.\n" "패스워드 초기화를 원치 않는 경우 본 메일을 무시해 주십시요. 변경을 요청할 경" "우 아래 링크를 클릭하세요." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "잊어버린 경우를 대비하여, 회원님의 사용자 이름은 %(username)s 입니다." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "비밀번호 초기화 이메일" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "비밀번호가 초기화 되었습니다." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "비밀번호가 설정되었습니다." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "비밀번호 설정" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "귀하 또는 다른 사람이 이메일 %(email)s로 계정에 접근하려 했기 때문에 이 이메" "일을 받으셨습니다. 그러나 저희 데이터베이스에는 해당 계정의 기록이 없습니다." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "이 작업이 귀하에 의해 이루어진 것이라면, 아래 링크를 사용하여 계정을 등록할 " "수 있습니다." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "알 수 없는 계정" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "이메일 계정" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "현재 이메일" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "변경 중" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "귀하의 이메일 주소는 아직 검증 중입니다." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "변경 취소" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "변경" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "이메일 변경" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "이메일 확인" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "%(email)s이 사용자 %(user_display)s의 이메일" "이 맞는지 확인해 주세요." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "%(email)s은 다른 계정에서 이미 확인되었기 때문에 확인할 수 없습니다." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "이 이메일 확인 링크는 만료되었거나 유효하지 않습니다. 새로운 이메일 확인 요청을 해주세요." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "계정이 없다면 %(link)s회원가입%(end_link)s을 진행하세요." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "패스키로 로그인" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "로그인 코드 받기" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "로그아웃" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "정말로 로그아웃 하시겠습니까?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "주 이메일은 제거할 수 없습니다 (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "%(email)s 으로 확인 메일이 전송되었습니다." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "%(email)s 을 확인하였습니다." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "%(email)s 을 제거하였습니다." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "%(name)s 으로 로그인 되었습니다." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "로그아웃 되었습니다." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "로그인 코드가 %(recipient)s(으)로 발송되었습니다." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "비밀번호가 성공적으로 변경되었습니다." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "비밀번호가 성공적으로 설정되었습니다." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "인증 코드가 %(phone)s(으)로 발송되었습니다." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "전화번호 %(phone)s이(가) 인증되었습니다." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "주 이메일이 지정되었습니다." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "비밀번호 변경" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "비밀번호를 잊으셨나요?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "비밀번호를 잊으셨나요? 아래에 당신의 이메일을 입력하시면, 비밀번호 초기화 이" "메일을 전송해 드리겠습니다." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "비밀번호 초기화" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "비밀번호 초기화에 문제가 있으시면 저희에게 연락주세요." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "메일을 전송하였습니다. 메일을 받지 못했다면 스팸 폴더를 확인해주세요.몇 분 후" "에도 메일을 받지 못하시면 저희에게 연락주세요." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "올비르지 않은 토큰" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "비밀번호 초기화 링크가 올바르지 않습니다. 다시 비밀번호 초기화 하세요." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "비밀번호가 변경되었습니다." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "비밀번호 설정" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "전화번호 변경" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "현재 전화번호" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "귀하의 휴대전화 번호는 아직 승인 전 입니다." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "전화번호 변경" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "패스워드 입력:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "비밀번호 없이 로그인할 수 있는 특별 코드를 받게 됩니다." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "코드 요청" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "다른 로그인 옵션" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "회원가입" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "회원가입" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "이미 계정이 있으신가요? 바로 %(link)s로그인%(end_link)s 하세요." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "패스키로 회원가입" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "패스키 회원가입" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "다른 옵션" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "회원가입 종료" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "죄송합니다. 회원가입은 현재 종료되었습니다." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "메모" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "%(user_display)s 로 이미 로그인 되어있습니다." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "경고: " #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "등록된 이메일이 없습니다. 알림, 비밀번호 초기화 등을 위해 이메일을 등록해야 " "합니다." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "이메일을 인증하세요" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "인증 메일이 전송되었습니다. 회원가입 완료를 위해 전송된 메일의 링크를 클릭하" "세요. 인증 메일을 받지 못했다면 스팸 폴더를 확인해주세요. 몇 분 후에도 메일" "이 전송되지 않으면 저희에게 연락주세요." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "해당 페이지는 계정 소유주가 맞는지\n" "확인해야합니다.\n" "이를 위해 이메일 주소 소유권을 확인해야 합니다." #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "인증 메일이 전송되었습니다. \n" "회원가입 완료를 위해 전송된 메일의 링크를 클릭하세요. 인증 메일을 받지 못했다" "면 스팸 폴더를 확인해주세요. 몇 분 후에도 메일이 전송되지 않으면 저희에게 연" "락주세요." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "메모: 이메일 변경이 가능합니" "다." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "메시지:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "메뉴:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "계정 연결" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "2단계 인증" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "세션" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "승인" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s이(가) 귀하의 %(site_name)s 계정에 접근하려고 합니다." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "디바이스 코드 입력" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "디바이스에 표시된 코드를 입력하세요." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "계속" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "디바이스 확인" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "이 디바이스를 승인하려면 %(client_name)s에 표시된 코드를 확인해 주세요." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "거부" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "디바이스 승인됨" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "%(client_name)s 디바이스가 성공적으로 승인되었습니다." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "디바이스 거부됨" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "%(client_name)s 디바이스에 대한 승인이 거부되었습니다." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "오류" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "로그인 유지" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "귀하의 계정은 2단계 인증으로 보호되고 있습니다. 인증 코드 입력해 주세요:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "새로운 2단계 인증 복구 코드 세트가 생성되었습니다." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "새로운 복구 코드가 생성되었습니다." #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "인증 앱이 활성화 되었습니다." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "인증 앱이 활성화 되었습니다." #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "인증 앱이 비활성화 되었습니다." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "인증 앱이 비활성화 되었습니다." #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "새로운 보안키가 추가되었습니다." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "보안키 추가됨" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "보안 키가 제거되었습니다." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "보안키 제거됨" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "인증 앱" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "인증 앱을 사용한 인증이 활성화 되었습니다." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "인증 앱이 활성화 되지 않았습니다." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "비활성화" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "활성화" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "보안키" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "%(count)s 를 보안 키로 설정하였습니다." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "보안키가 추가되지 않았습니다." #: templates/mfa/index.html:62 msgid "Manage" msgstr "관리" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "추가" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "복구 코드" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "총 %(total_count)s개 계정 중 %(unused_count)s개의 계정이 복구 코드 설정 가능" "합니다." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "복구 코드가 설정되지 않았습니다." #: templates/mfa/index.html:96 msgid "View" msgstr "보기" #: templates/mfa/index.html:102 msgid "Download" msgstr "다운로드" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "생성" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "새로운 복구 코드 세트가 생성되었습니다." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "보안 키가 추가 되었습니다." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "보안 키가 삭제되었습니다." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "인증 코드 입력:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "귀하의 계정을 위한 새로운 복구 코드 세트를 생성하려고 합니다." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "이 작업은 기존 코드를 무효화 합니다." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "확실하세요?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "사용되지 않은 코드" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "코드 다운로드" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "새로운 코드 생성" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "인증 앱 활성화" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "귀하의 계정을 2단계 인증으로 보호하려면 아래의 QR 코드를 인증기 앱으로 스캔하" "세요. 그런 다음, 앱에서 생성된 인증 코드를 아래에 입력하세요." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "인증 secret" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "이 secret을 저장해 두면 나중에 인증기 앱을 재설치할 때 사용할 수 있습니다." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "인증 앱 비활성화" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "인증 앱 기반 인증을 비활성화하려고 합니다. 확실하세요?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "이 브라우저를 신뢰하시겠습니까?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "이 브라우저를 신뢰하면 다음에 로그인할 때 인증 코드를 요청하지 않습니다." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "%(period)s 동안 신뢰" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "신뢰하지 않음" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "보안키 추가" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "보안키 제거" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "정말로 보안키를 제거하시겠습니까?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "사용" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "패스키" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "보안 키" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "이 키는 패스키인지 여부를 나타내지 않습니다." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "지정되지 않음" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "%(created_at)s에 추가됨" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "%(last_used)s에 마지막으로 사용됨" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "변경" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "보안 키 변경" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "저장" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "패스키 생성" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "귀하의 계정을 위한 보안 키 인증을 설정하세요. 그리고 나서 보안 키를 추가적으" "로 설정할 수 있습니다." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "생성" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "이 기능은 자바스크립트를 요구합니다." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "소셜 로그인 실패" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "소셜 계정을 통해 로그인 하는 도중 오류가 발생했습니다." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "다음 소셜 계정들을 통해 로그인 할 수 있습니다:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "해당 계정에 연결되어있는 소셜 계정이 없습니다." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "소셜 계정을 추가하세요." #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "%(provider)s로 부터 소셜 계정이 귀하의 계정에 연결되었습니다." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "소셜 계정 연결됨." #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "%(provider)s로 부터 소셜 계정이 귀하의 계정에서 연결이 해제되었습니다." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "소셜 계정이 연결 해제됨" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "%(provider)s 계정 연결" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "%(provider)s에서 제공하는 소셜 계정을 연결하려 합니다." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "%(provider)s을 통한 로그인" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "소셜 %(provider)s의 계정을 사용해 로그인을 진행하려 합니다." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "로그인 취소됨" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "기존 계정중 하나를 사용한 로그인을 취소하였습니다. 실수로 인한 경우, 로그인을 진행해 주세요." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "소셜 계정이 연결되었습니다." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "소셜 계정 연결이 해제되었습니다." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "%(provider_name)s 의 계정을 이용하여 %(site_name)s 으로 로그인하려 합니다.\n" "마지막으로 다음 폼을 작성해주세요:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "또는 소셜을 사용하세요." #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "모든 다른 세션에서 로그아웃되었습니다." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "시작 시작" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP 주소" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "브라우저" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "마지막으로 본 시간" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "현재" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "다른 세션에서 로그아웃" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "사용자 세션" #: usersessions/models.py:94 msgid "session key" msgstr "세션 키" #~ msgid "Account Connection" #~ msgstr "계정 연결" ================================================ FILE: allauth/locale/ky/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2024-04-20 21:44+0200\n" "Last-Translator: Murat Jumashev \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Poedit 1.5.4\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Бул эсеп учурда активдүү эмес." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Негизги эмейл даректи алып салуу мүмкүн эмес." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Бул эмейл дарек ушул эсеп менен буга чейин туташтырылган." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Сиз берген эмейл дарек жана/же купуя туура эмес." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Сиз берген телефон номери жана/же купуя туура эмес." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Мындай эмейл менен катталган колдонуучу бар." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Учурдагы купуяңызды жазыңыз." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Код туура эмес." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Купуя туура эмес." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Ачкыч туура эмес же мөөнөтү өткөн." #: account/adapter.py:79 msgid "Invalid login." msgstr "Кирүү туура эмес." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Купуяны жаңыртуу токени туура эмес." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Сиз %d эмейл даректен ашык кошо албайсыз." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Мындай телефон номери менен катталган колдонуучу бар." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Өтө көп жолу кирүү аракеттери жасалды. Кайрадан аракеттениңиз." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Эмейл дарек эч бир колдонуучу эсебине байланган эмес." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Телефон номери эч бир колдонуучу эсебине байланган эмес." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Негизги эмейл дарегиңиз дурусталышы керек." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Бул атты колдонуу мүмкүн эмес. Башкасын тандаңыз." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Сиз берген колдонуучу аты жана/же купуя туура эмес." #: account/adapter.py:98 msgid "Please select only one." msgstr "Бирөөнү гана тандаңыз." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Жаңы маани азыркысынан айырмаланышы керек." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Сабырдуу болуңуз, сиз өтө көп сурам жөнөтүп жатасыз." #: account/adapter.py:826 msgid "Use your password" msgstr "Купуяңызды колдонуңуз" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Аутентификатор колдонмосун же кодду колдонуңуз" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Коопсуздук ачкычын колдонуңуз" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} дурусталган деп белгиленди." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "{email} дурусталган деп белгилөө ишке ашкан жок." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Тандалган эмейл даректерди дурусталган деп белгилөө" #: account/apps.py:11 msgid "Accounts" msgstr "Эсептер" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "Эмейл" #: account/fields.py:19 msgid "Email address" msgstr "Эмейл дарек" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Өлкө коду менен телефон номерин жазыңыз (мис. АКШ үчүн +1)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Телефон" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Сиз ошол эле купуяны кайрадан териңиз." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Купуя" #: account/forms.py:67 msgid "Remember Me" msgstr "Мени эстеп кал" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Колдонуучу аты" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Кирүү" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Колдонуучу аты, эмейл же телефон" #: account/forms.py:117 msgid "Username or email" msgstr "Колдонуучу аты же эмейл" #: account/forms.py:119 msgid "Username or phone" msgstr "Колдонуучу аты же телефон" #: account/forms.py:121 msgid "Email or phone" msgstr "Эмейл же телефон" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Купуяңызды унуттуңузбу?" #: account/forms.py:287 msgid "Email (again)" msgstr "Эмейл (кайрадан)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Эмейл дарек ырастоо" #: account/forms.py:302 msgid "Email (optional)" msgstr "Эмейл (милдеттүү эмес)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Колдонуучу аты (милдеттүү эмес)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Ар бир жолу бирдей эмейлди терүүңүз керек." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Купуя (дагы бир жолу)" #: account/forms.py:591 msgid "Current Password" msgstr "Азыркы купуя" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Жаңы купуя" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Жаңы купуя (кайрадан)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Код" #: account/models.py:23 msgid "user" msgstr "колдонуучу" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "эмейл дарек" #: account/models.py:31 msgid "verified" msgstr "дурусталган" #: account/models.py:32 msgid "primary" msgstr "негизги" #: account/models.py:38 msgid "email addresses" msgstr "эмейл даректер" #: account/models.py:142 msgid "created" msgstr "түзүлгөн" #: account/models.py:143 msgid "sent" msgstr "жөнөтүлгөн" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "ачкыч" #: account/models.py:149 msgid "email confirmation" msgstr "эмейл ырастоо" #: account/models.py:150 msgid "email confirmations" msgstr "эмейл ырастоолор" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Колдонуучу ID'ңизди көрүү" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Эмейл дарегиңизди көрүү" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Негизги профил маалыматыңызды көрүү" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Уруксаттарды берүү" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "'URI белгилерине уруксат берүү' иштетилмегенче, белгилерге (*) уруксат " "берилбейт." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' бирден көп белги (*) камтыйт. Бир URI үчүн бир гана белгиге уруксат " "берилет." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Белгилерге (*) URI'дин хост аты бөлүгүндө гана уруксат берилет." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Авторизация коду" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Түзмөк коду" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Кардар мандаттары" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Жаңыртуу токени" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Жашыруун" #: idp/oidc/models.py:44 msgid "Public" msgstr "Жалпыга ачык" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Кардар суроого укуктуу чөйрөлөр. Бир сапка бир маани жазыңыз, мис.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Эгерде кардар чөйрө көрсөтпөсө, бул демейки чөйрөлөр колдонулат. Бир сапка " "бир маани жазыңыз, мис.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Уруксат берилген грант типтеринин тизмеси. Бир сапка бир маани жазыңыз, " "мис.: authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Кайчылаш суроолор үчүн уруксат берилген булактардын тизмеси, бир сапка " "бирден." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Кайра багыттоо URI'лерде жана CORS булактарында белгилерге (*) уруксат " "берүү. Иштетилгенде, URI'лер субдомендерди дал келтирүү үчүн бир жылдызча " "камтый алат." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Уруксат берилген жооп типтеринин тизмеси. Бир сапка бир маани жазыңыз, мис.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "кардар" #: idp/oidc/models.py:116 msgid "clients" msgstr "кардарлар" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Эки кадамдуу аутентификация менен корголгон эсепке эмейл дарек кошо албайсыз." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Эки кадамдуу аутентификацияны өчүрө албайсыз." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Эки кадамдуу аутентификация иштетилбей калыбына келтирүү коддорун түзө " "албайсыз." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Эмейл дарегиңизди дурусталмайынча эки кадамдуу аутентификацияны иштете " "албайсыз." #: mfa/adapter.py:141 msgid "Master key" msgstr "Башкы ачкыч" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Камдык ачкыч" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Ачкыч №{number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Калыбына келтирүү коддору" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP Аутентификатор" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Аутентификатор коду" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Купуясыз" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Купуясыз режимди иштетүү бул ачкычты гана колдонуп кирүүгө мүмкүнчүлүк " "берет, бирок биометрика же PIN коргоо сыяктуу кошумча талаптарды коёт." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Бул эмейл менен башка эсеп катталган. Алгач ошол эсепке кирип, %s эсебиңизди " "туташтырыңыз." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Токен туура эмес." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Сиздин эсебиңизде купуя орнотулган эмес." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Сиздин эсебиңизде дурусталган эмейл даректер жок." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Акыркы калган үчүнчү тарап эсебиңизди ажырата албайсыз." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Бул үчүнчү тарап эсеби башка эсепке туташтырылган." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Социалдык эсептер" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "провайдер" #: socialaccount/models.py:53 msgid "provider ID" msgstr "провайдер ID" #: socialaccount/models.py:57 msgid "name" msgstr "аты" #: socialaccount/models.py:59 msgid "client id" msgstr "кардар id'си" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "Колдонмо ID'си, же керектөөчү ачкычы" #: socialaccount/models.py:64 msgid "secret key" msgstr "жашыруун ачкыч" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API, кардар же керектөөчүнүн жашыруун ачкычы" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Ачкыч" #: socialaccount/models.py:82 msgid "social application" msgstr "социалдык колдонмо" #: socialaccount/models.py:83 msgid "social applications" msgstr "социалдык колдонмолор" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "акыркы кириши" #: socialaccount/models.py:121 msgid "date joined" msgstr "кошулган күнү" #: socialaccount/models.py:122 msgid "extra data" msgstr "кошумча маалымат" #: socialaccount/models.py:126 msgid "social account" msgstr "социалдык эсеп" #: socialaccount/models.py:127 msgid "social accounts" msgstr "социалдык эсептер" #: socialaccount/models.py:161 msgid "token" msgstr "токен" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) же жетки токени (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "токендин жашыруун ачкычы" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) же жаңыртуу токени (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "мөөнөтү аяктайт" #: socialaccount/models.py:175 msgid "social application token" msgstr "социалдык колдонмо токени" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "социалдык колдонмо токендери" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Профил маалыматы туура эмес" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Кирүү" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Жокко чыгаруу" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "\"%s\" тарабынан сурам токенин алууда туура эмес жооп келди. Жооп: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "\"%s\" тарабынан жетки токенин алууда туура эмес жооп келди." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "\"%s\" үчүн сурам токени сакталган жок." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "\"%s\" үчүн жетки токени сакталган жок." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "\"%s\" тарабындагы жеке ресурстарга жетки жок." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "\"%s\" тарабынан сурам токенин алууда туура эмес жооп келди." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Эсеп активдүү эмес" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Бул эсеп активдүү эмес." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Биз %(recipient)s дарегине код жөнөттүк. Коддун мөөнөтү жакында бүтөт, " "андыктан тезирээк жазыңыз." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Ырастоо" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Жаңы код сурануу" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Кирүүнү ырастаңыз" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Эсебиңизди коргоо үчүн кайрадан аутентификация өтүңүз." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Альтернативалуу варианттар" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Эмейл дурустоо" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Эмейл дурустоо кодун жазыңыз" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Башка эмейл дарек колдонуңуз" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Кирүү" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Кирүү кодун жазыңыз" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Купуяны жаңыртуу" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Купуяны жаңыртуу кодун жазыңыз" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Телефон дурустоо" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Телефон дурустоо кодун жазыңыз" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Башка телефон номерин колдонуңуз" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Эмейл даректер" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Төмөнкү эмейл даректер сиздин эсебиңизге байланган:" #: templates/account/email.html:25 msgid "Verified" msgstr "Дурусталган" #: templates/account/email.html:29 msgid "Unverified" msgstr "Дурустала элек" #: templates/account/email.html:34 msgid "Primary" msgstr "Негизги" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Негизги кылуу" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Дурустоо катын кайрадан жиберүү" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Алып салуу" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Эмейл дарек кошуу" #: templates/account/email.html:70 msgid "Add Email" msgstr "Эмейл кошуу" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Тандалган эмейл даректи алып салууну дурустайсызбы?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Сиз же башка бирөө төмөнкү эмейл дарек менен\n" "эсеп каттоого аракет кылгандыктан бул катты алып жатасыз:\n" "\n" "%(email)s\n" "\n" "Бирок, бул эмейл дарек менен эсеп мурунтан эле бар. Эгерде сиз муну\n" "унутуп калган болсоңуз, эсебиңизди калыбына келтирүү үчүн купуя жаңыртуу\n" "процедурасын колдонуңуз:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Эсеп мурунтан эле бар" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "%(site_name)s сайтынан салам!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "%(site_name)s сайтын колдонгонуңузга алкыш!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "Эсебиңизде төмөнкү өзгөрүү болгондуктан бул катты алып жатасыз:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Эгерде бул өзгөрүүнү тааныбасаңыз, дароо тиешелүү коопсуздук чараларын " "көрүңүз. Эсебиңиздеги өзгөрүү төмөнкүдөн келген:\n" "\n" "- IP дарек: %(ip)s\n" "- Браузер: %(user_agent)s\n" "- Дата: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Эмейлиңиз %(from_email)s дарегинен %(to_email)s дарегине өзгөртүлдү." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Эмейл өзгөртүлдү" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Эмейлиңиз дурусталды." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Эмейл ырастоо" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "%(user_display)s деген колдонуучу %(site_domain)s деген сайтта эсеп каттоо " "үчүн сиздин эмейл дарегиңизди берген болгондуктан, бул катты алып жатасыз." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Эмейлиңизди дурустоо кодуңуз төмөндө берилген. Аны ачык браузер терезеңизге " "жазыңыз." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Бул туура экенин ырастоо үчүн %(activate_url)s дарегине өтүңүз" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Эмейл дарегиңизди ырастаңыз" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "%(deleted_email)s эмейл дареги эсебиңизден алып салынды." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Эмейл алып салынды" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "Кирүү кодуңуз төмөндө берилген. Аны ачык браузер терезеңизге жазыңыз." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Эгерде бул аракетти сиз баштабаган болсоңуз, бул катка көңүл бурбай коюсаңыз " "болот." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Кирүү коду" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Купуяңыз өзгөртүлдү." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Купуя өзгөртүлдү" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Купуяны жаңыртуу кодуңуз төмөндө берилген. Аны ачык браузер терезеңизге " "жазыңыз." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Купуяны жаңыртуу коду" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Сиз же башка бирөө сиздин эсебиңиздин купуясын жаңыртуу сурамын " "жөнөткөндүктөн бул катты алып жатасыз.\n" "Эгерде сурамды сиз жөнөтпөгөн болсоңуз, бул катка көңүл бурбай эле коюңуз. " "Купуяны жаңыртуу үчүн төмөндөгү шилтемени басыңыз." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Эстей албай жатсаңыз, сиздин колдонуучу атыңыз %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Купуяны жаңыртуу эмейли" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Купуяңыз жаңыртылды." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Купуяңыз коюлду." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Купуя коюлду" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Сиз же башка бирөө %(email)s эмейл дареги менен эсепке кирүүгө аракет " "кылгандыктан бул катты алып жатасыз. Бирок, биздин базада мындай эсеп жок." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Эгерде бул сиз болсоңуз, төмөнкү шилтеме аркылуу эсеп каттасаңыз болот." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Белгисиз эсеп" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Эмейл дарек" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Азыркы эмейл" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Өзгөртүлүүдө" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Эмейл дарегиңиз дагы деле дурусталбай турат." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Өзгөртүүнү жокко чыгаруу" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Өзгөртүү" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Эмейлди өзгөртүү" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Эмейл даректи ырастаңыз" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "%(email)s деген эмейл дарек " "%(user_display)s колдонуучусуна таандык экенин ырастаңыз." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "%(email)s дарегин ырастоо мүмкүн эмес, анткени ал башка эсеп тарабынан " "мурунтан эле ырасталган." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Бул эмейлди ырастоо шилтемесинин мөөнөтү өтүп кеткен же ал туура эмес. Эмейлди ырастоо сурамын кайрадан жөнөтүңүз." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Эгерде сиз эсеп түзө элек болсоңуз, анда биринчи " "%(link)sкатталыңыз%(end_link)s" #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Passkey менен кирүү" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Мага кирүү кодун жөнөтүңүз" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Чыгуу" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Сиз чыгып жатканыңызды дурустайсызбы?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Негизги эмейл даректи алып салуу мүмкүн эмес (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Ырастоо эмейли %(email)s дарегине жөнөтүлдү." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Сиз %(email)s дарегин ырастадыңыз." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "%(email)s дарегин алып салдык." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "%(name)s аты менен ийгиликтүү кирдик." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Сиз чыгып кеттиңиз." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Кирүү коду %(recipient)s дарегине жөнөтүлдү." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Купуя ийгиликтүү өзгөртүлдү." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Купуя ийгиликтүү орнотулду." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Дурустоо коду %(phone)s номерине жөнөтүлдү." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Сиз %(phone)s телефон номерин дурустадыңыз." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Негизги эмейл орнотулду." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Купуяны өзгөртүү" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Купуяңызды унуттуңузбу?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Купуяңызды унуттуңузбу? Эмейл дарегиңизди төмөндө жазсаңыз, биз сизге кат " "жөнөтүп, аны жаңыртканга жардам беребиз." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Купуямды жаңырт" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "Купуяны жаңыртууда суроолор пайда болсо, бизге кайрылыңыз." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Биз сизге кат жөнөттүк. Эгерде кат кабыл алынбаса, спам папкаңызды " "текшериңиз. Кат бир нече мүнөттө келбесе, бизге кайрылыңыз." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Токен туура эмес" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Купуя жаңыртуу шилтемеси туура эмес экен, ал мурун колдонулса керек. Купуяны жаңыртуу сурамын кайрадан " "жөнөтүңүз." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Купуяңыз эми өзгөртүлдү." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Купуя орнотуу" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Телефонду өзгөртүү" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Азыркы телефон" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Телефон номериңиз дагы деле дурусталбай турат." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Телефонду өзгөртүү" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Купуяңызды жазыңыз:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Купуясыз кирүү үчүн атайын код аласыз." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Код сурануу" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Кирүүнүн башка варианттары" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Катталуу" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Катталуу" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Эсебиңиз барбы? Анда %(link)sкириңиз%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Passkey менен катталуу" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Passkey менен катталуу" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Башка варианттар" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Катталуу жабык" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Кечирим сурайбых, бирок учурда катталуу жабык." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Эскертүү" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Сиз %(user_display)s катары кирип турасыз." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Эскертүү:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Сизде азыр бир дагы эмейл катталган эмес. Билдирүүлөрдү алуу, купуяны " "жаңыртуу ж.б. үчүн каттап коюңуз." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Эмейл дарегиңизди дурустап бериңиз" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Биз эмейлиңизди дурустоо үчүн кат жөнөттүк. Каттоодон өтүүнү аяктоо үчүн " "берилген шилтемени басыңыз. Дурустоо каты негизги кутуңузда жок болсо, спам " "папкасын текшериңиз. Кат бир нече мүнөттө келбесе, бизге кайрылыңыз." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Сайттын бул бөлүгүнө өтүш үчүн сиз чын эле сиз айткан\n" "адам экениңизди такташыбыз керек. Ал үчүн эмейл\n" "дарек сизге таандык экенин дурусташыңыз керек. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Биз сизге кат жөнөттүк, дурустоо үчүн.\n" "Ичинде берилген шилтемени басыңыз. Дурустоо каты негизги кутуңузда жок " "болсо, спам папкасын текшериңиз. Болбосо,\n" "кат бир нече мүнөттө келбей калса, бизге кайрылыңыз." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Эскертүү: эмейл дарегиңизди дагы деле өзгөртсөңүз болот." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Билдирүүлөр:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Меню:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Эсеп байланыштары" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Эки кадамдуу аутентификация" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Сессиялар" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Уруксат берүү" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s сиздин %(site_name)s эсебиңизге кирүүнү каалайт." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Түзмөк кодун жазыңыз" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Түзмөгүңүздө көрсөтүлгөн кодду жазыңыз." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Улантуу" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Түзмөктү ырастаңыз" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Бул түзмөккө уруксат берүү үчүн %(client_name)s түзмөгүңүздө көрсөтүлгөн " "кодду ырастаңыз." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Баш тартуу" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Түзмөккө уруксат берилди" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Сиз %(client_name)s түзмөгүңүзгө ийгиликтүү уруксат бердиңиз." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Түзмөккө баш тартылды" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "%(client_name)s түзмөгүңүзгө уруксат берүү четке кагылды." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Ката" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Кирип калуу" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Эсебиңиз эки кадамдуу аутентификация менен корголгон. Аутентификатор кодун " "жазыңыз:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Эки кадамдуу аутентификациянын калыбына келтирүү коддорунун жаңы тобу " "түзүлдү." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Жаңы калыбына келтирүү коддору түзүлдү" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Аутентификатор колдонмосу иштетилди." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Аутентификатор колдонмосу иштетилди" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Аутентификатор колдонмосу өчүрүлдү." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Аутентификатор колдонмосу өчүрүлдү" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Жаңы коопсуздук ачкычы кошулду." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Коопсуздук ачкычы кошулду" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Коопсуздук ачкычы алып салынды." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Коопсуздук ачкычы алып салынды" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Аутентификатор колдонмосу" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Аутентификатор колдонмосу менен аутентификация иштеп жатат." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Аутентификатор колдонмосу иштебей жатат." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Өчүрүү" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Иштетүү" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Коопсуздук ачкычтары" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "%(count)s коопсуздук ачкычы кошулду." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Коопсуздук ачкычтары кошулган эмес." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Башкаруу" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Кошуу" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Калыбына келтирүү коддору" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "%(total_count)s калыбына келтирүү кодунан %(unused_count)s жеткиликтүү." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Калыбына келтирүү коддору орнотулган эмес." #: templates/mfa/index.html:96 msgid "View" msgstr "Көрүү" #: templates/mfa/index.html:102 msgid "Download" msgstr "Жүктөп алуу" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Түзүү" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Калыбына келтирүү коддорунун жаңы тобу түзүлдү." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Коопсуздук ачкычы кошулду." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Коопсуздук ачкычы алып салынды." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Аутентификатор кодун жазыңыз:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Сиз эсебиңиз үчүн калыбына келтирүү коддорунун жаңы тобун түзөсүз." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Бул аракет учурдагы коддоруңузду жараксыз кылат." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Ишенимдүүсүзбү?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Колдонулбаган коддор" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Коддорду жүктөп алуу" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Жаңы коддорду түзүү" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Аутентификатор колдонмосун иштетүү" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Эсебиңизди эки кадамдуу аутентификация менен коргоо үчүн, төмөндөгү QR кодду " "аутентификатор колдонмоңуз менен сканерлеңиз. Андан кийин, колдонмо " "тарабынан түзүлгөн дурустоо кодун жазыңыз." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Аутентификатор жашыруун ачкычы" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Бул жашыруун ачкычты сактап, кийинчерээк аутентификатор колдонмоңузду кайра " "орнотуу үчүн колдонсоңуз болот." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Аутентификатор колдонмосун өчүрүү" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Аутентификатор колдонмосуна негизделген аутентификацияны өчүрөсүз. " "Ишенимдүүсүзбү?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Бул браузерге ишенесизби?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Эгерде бул браузерге ишенсеңиз, кийинки жолу кирүүдө дурустоо коду суралбайт." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "%(period)s ишенүү" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Ишенбөө" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Коопсуздук ачкычын кошуу" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Коопсуздук ачкычын алып салуу" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Бул коопсуздук ачкычын алып салууну каалайсызбы?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Колдонуу" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Passkey" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Коопсуздук ачкычы" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Бул ачкыч passkey экенин көрсөтпөйт." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Белгиленбеген" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "%(created_at)s кошулган" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Акыркы жолу колдонулган %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Өзгөртүү" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Коопсуздук ачкычын өзгөртүү" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Сактоо" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Passkey түзүү" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Сиз эсебиңиз үчүн passkey түзөсүз. Кийинчерээк кошумча ачкычтарды коше " "аласыз, андыктан аларды айырмалоо үчүн сүрөттөмө ат колдонсоңуз болот." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Түзүү" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Бул функция JavaScript талап кылат." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Үчүнчү тарап аркылуу кирүү ийгиликсиз" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "Үчүнчү тарап эсебиңиз аркылуу кирүүдө ката кетти." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Эсебиңизге кийинки үчүнчү тарап эсептердин бирин колдонуп кирсеңиз болот:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Азырынча эсебиңизге үчүнчү тарап эсептери байланган эмес." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Үчүнчү тарап эсебин кошуу" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "" "%(provider)s тарабынан үчүнчү тарап эсеби сиздин эсебиңизге туташтырылды." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Үчүнчү тарап эсеби туташтырылды" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" "%(provider)s тарабынан үчүнчү тарап эсеби сиздин эсебиңизден ажыратылды." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Үчүнчү тарап эсеби ажыратылды" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "%(provider)s туташтыруу" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Сиз %(provider)s тарабынан жаңы үчүнчү тарап эсебин туташтырасыз." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "%(provider)s аркылуу кирүү" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "Сиз %(provider)s тарабынан үчүнчү тарап эсеби менен кирүүдөсүз." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Кирүү токтотулду" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Эсептериңизин бирөөсүн колдонуп сайтыбызга кирүүдөн баш тарттыңыз. Аны " "байкабастан кылып алсаңыз, кирүүнү улантсаңыз " "болот." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Үчүнчү тарап эсеби туташтырылды." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Үчүнчү тарап эсеби ажыратылды." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Сиз азыр %(provider_name)s эсебиңизди колдонуп, %(site_name)s\n" "сайтына кирейин деп турасыз. Акыркы кадам катары кийинки калыпты\n" "толтуруп коюңузду суранабыз :" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Же үчүнчү тарапты колдонуңуз" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Башка бардык сессиялардан чыгарылды." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Башталган убакыт" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP дарек" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Браузер" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Акыркы жолу көрүлгөн" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Азыркы" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Башка сессиялардан чыгуу" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Колдонуучу сессиялары" #: usersessions/models.py:94 msgid "session key" msgstr "сессия ачкычы" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Эсеп байланыштары" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Купуя жок дегенде {0} белгиден турушу керек." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "%(site_name)s сайтынан салам!\n" #~ "\n" #~ "Сиз бул катты %(site_domain)s сайтынан кимдир бирөө эсебиңиздин купуясын " #~ "жаңыртуу сурамын жөнөткөндүктөн алып жататсыз.\n" #~ "Эгерде сурамды сиз жөнөтпөгөн болсоңуз, бул катка көңүл бурбай эле " #~ "коюңуз. Купуяны жаңыртуу үчүн төмөндөгү шилтемени басыңыз." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "Төмөнкү эмейл даректер сиздин эсебиңизге байланган:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Эмейл даректи ырастаңыз" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Тышкаркы кызматтарда катталган\n" #~ "эсебиңиз аркылуу кириңиз. Же, %(site_name)s \n" #~ "сайтына %(link)sкатталып%(end_link)s\n" #~ "\"төмөн жактан кириңиз:" #~ msgid "or" #~ msgstr "же" #~ msgid "change password" #~ msgstr "купуяны өзгөртүү" #~ msgid "OpenID Sign In" #~ msgstr "OpenID менен кирүү" #~ msgid "This email address is already associated with another account." #~ msgstr "Бул эмейл дарек башка бир эсеп менен буга чейин туташтырылган." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Биз сизге кат жөнөттүк. Бир нече мүнөттүн ичинде келбей калса бизге " #~ "кайрылыңыз." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "Сиз берген логин жана/же купуя туура эмес." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "" #~ "Колдонуучу аттары тамгалардан, сандардан жана @/./+/-/_ белгилеринен гана " #~ "тура алат." ================================================ FILE: allauth/locale/lt/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2024-04-20 22:02+0200\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < " "11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? " "1 : n % 1 != 0 ? 2: 3);\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Šiuo metu ši paskyra yra neaktyvi." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Negalite pašalinti pagrindinio el. pašto." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Šis el. pašto adresas jau susietas su šia paskyra." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Pateiktas el. pašto adresas ir/arba slaptažodis yra neteisingi." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Nurodytas telefono numeris ir/arba slaptažodis yra neteisingi." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Šiuo el. pašto adresu jau yra užsiregistravęs kitas naudotojas." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Prašome įvesti esamą jūsų slaptažodį." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Neteisingas kodas." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Neteisingas slaptažodis." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Neteisingas arba pasenęs raktas." #: account/adapter.py:79 msgid "Invalid login." msgstr "Neteisingas prisijungimas." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Neteisingas slaptažodžio atstatymo atpažinimo ženklas." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Negalima pridėti daugiau nei %d el. pašto adresų." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Šiuo telefono numeriu jau yra užsiregistravęs kitas naudotojas." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Per daug nepavykusių prisijungimo bandymų. Bandykite vėliau." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "El. pašto adresas nėra susietas su jokia naudotojo paskyra." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Telefono numeris nėra susietas su jokia naudotojo paskyra." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Pirminis el. pašto adresas turi būti patvirtintas." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "" "Šis naudotojo vardas negalimas. Prašome pasirinkti kitą naudotojo vardą." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Pateiktas naudotojo vardas ir/arba slaptažodis yra neteisingi." #: account/adapter.py:98 msgid "Please select only one." msgstr "Prašome pasirinkti tik vieną." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Nauja reikšmė turi skirtis nuo dabartinės." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Būkite kantrūs, siunčiate per daug užklausų." #: account/adapter.py:826 msgid "Use your password" msgstr "Naudokite slaptažodį" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Naudokite autentifikacijos programą arba kodą" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Naudokite saugumo raktą" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} pažymėtas kaip patvirtintas." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Nepavyko pažymėti {email} kaip patvirtinto." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Pažymėti pasirinktus el. pašto adresus, kaip patvirtintus" #: account/apps.py:11 msgid "Accounts" msgstr "Paskyros" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "El. paštas" #: account/fields.py:19 msgid "Email address" msgstr "El. pašto adresas" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Įveskite telefono numerį su šalies kodu (pvz., +1 JAV)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefonas" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Turite įvesti tą patį slaptažodį kiekvieną kartą." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Slaptažodis" #: account/forms.py:67 msgid "Remember Me" msgstr "Prisimink mane" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Naudotojo vardas" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Prisijungimo vardas" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Naudotojo vardas, el. paštas arba telefonas" #: account/forms.py:117 msgid "Username or email" msgstr "Naudotojo vardas arba el. paštas" #: account/forms.py:119 msgid "Username or phone" msgstr "Naudotojo vardas arba telefonas" #: account/forms.py:121 msgid "Email or phone" msgstr "El. paštas arba telefonas" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Pamiršote slaptažodį?" #: account/forms.py:287 msgid "Email (again)" msgstr "El. paštas (pakartoti)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "El. pašto patvirtinimas" #: account/forms.py:302 msgid "Email (optional)" msgstr "El. paštas (neprivalomas)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Naudotojo vardas (neprivalomas)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Turite įvesti tą patį el. pašto adresą kiekvieną kartą." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Slaptažodis (pakartoti)" #: account/forms.py:591 msgid "Current Password" msgstr "Esamas slaptažodis" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Naujas slaptažodis" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Naujas slaptažodis (pakartoti)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Kodas" #: account/models.py:23 msgid "user" msgstr "naudotojas" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "el. pašto adresas" #: account/models.py:31 msgid "verified" msgstr "patvirtintas" #: account/models.py:32 msgid "primary" msgstr "pirminis" #: account/models.py:38 msgid "email addresses" msgstr "el. pašto adresai" #: account/models.py:142 msgid "created" msgstr "sukurtas" #: account/models.py:143 msgid "sent" msgstr "išsiųstas" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "raktas" #: account/models.py:149 msgid "email confirmation" msgstr "el. pašto patvirtinimas" #: account/models.py:150 msgid "email confirmations" msgstr "el. pašto patvirtinimai" #: headless/apps.py:7 msgid "Headless" msgstr "Begalvis" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Peržiūrėkite savo naudotojo ID" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Peržiūrėkite savo el. pašto adresą" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Peržiūrėkite savo pagrindinę profilio informaciją" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Suteikti leidimus" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Pakaitos simboliai neleidžiami, nebent įjungta 'Leisti URI pakaitos " "simbolius'." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' turi daugiau nei vieną pakaitos simbolį (*). Leidžiamas tik vienas " "pakaitos simbolis vienam URI." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Pakaitos simboliai leidžiami tik URI prieglobos pavadinimo dalyje." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Autorizacijos kodas" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Įrenginio kodas" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Kliento kredencialai" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Atnaujinimo žetonas" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Konfidencialus" #: idp/oidc/models.py:44 msgid "Public" msgstr "Viešas" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Apimtys, kurias klientui leidžiama prašyti. Pateikite vieną reikšmę " "eilutėje, pvz.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Jei klientas nenurodo jokios apimties, naudojamos šios numatytosios apimtys. " "Pateikite vieną reikšmę eilutėje, pvz.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Leidžiamų suteikimo tipų sąrašas. Pateikite vieną reikšmę eilutėje, pvz.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Leidžiamų šaltinių sąrašas tarpdomeniniams užklausoms, po vieną eilutėje." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Leisti pakaitos simbolius (*) peradresavimo URI ir CORS šaltiniuose. Kai " "įjungta, URI gali turėti vieną žvaigždutę subdomenams atitikti." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Leidžiamų atsakymo tipų sąrašas. Pateikite vieną reikšmę eilutėje, pvz.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "klientas" #: idp/oidc/models.py:116 msgid "clients" msgstr "klientai" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Jūs negalite pridėti el. pašto adreso į dviguba autentifikacija saugomą " "paskyrą." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Jūs negalite deaktyvuoti dvigubos autentifikacijos." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Jūs negalite generuoti atstatymo kodų be įjungtos dvigubos autentifikacijos." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Jūs negalite aktyvuoti dvigubos autentifikacijos kol nepatvirtinsite savo " "el. pašto." #: mfa/adapter.py:141 msgid "Master key" msgstr "Pagrindinis raktas" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Atsarginis raktas" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Rakto nr. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Atkūrimo kodai" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP autentikatorius" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Autentifikatoriaus kodas" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Be Slaptažodžio" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Prisijungimo be slaptažodžio režimo įjungimas leidžia prisijungti naudojant " "tik šį raktą, tačiau nustato papildomus reikalavimus, tokius kaip biometrika " "arba PIN apsauga." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Paskyra su šiuo el. pašto adresu jau egzistuoja. Prašome pirmiausia " "prisijungti prie tos paskyros ir tada prijunkite %s paskyrą." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Klaidinga atpažinimo žymė" #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Jūsų paskyra neturi nustatyto slaptažodžio." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Jūsų paskyra neturi patvirtinto el. pašto adreso." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Negalite atjungti paskutinės trečiosios šalies paskyros." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Trečiosios šalies paskyra jau yra prijungta prie kitos paskyros." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Socialinės paskyros" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "tiekėjas" #: socialaccount/models.py:53 msgid "provider ID" msgstr "tiekėjo ID" #: socialaccount/models.py:57 msgid "name" msgstr "pavadinimas" #: socialaccount/models.py:59 msgid "client id" msgstr "kliento id" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "App ID arba consumer key" #: socialaccount/models.py:64 msgid "secret key" msgstr "secret key" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API secret, client secret, arba consumer secret" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Raktas" #: socialaccount/models.py:82 msgid "social application" msgstr "socialinė programėlė" #: socialaccount/models.py:83 msgid "social applications" msgstr "socialinės programėlės" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "paskutinis prisijungimas" #: socialaccount/models.py:121 msgid "date joined" msgstr "registracijos data" #: socialaccount/models.py:122 msgid "extra data" msgstr "papildomi duomenys" #: socialaccount/models.py:126 msgid "social account" msgstr "socialinė paskyra" #: socialaccount/models.py:127 msgid "social accounts" msgstr "socialinės paskyros" #: socialaccount/models.py:161 msgid "token" msgstr "atpažinimo ženklas" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) arba prieigos atpažinimo ženklas (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "token secret" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "" "\"oauth_token_secret\" (OAuth1) arba atnaujintas atpažinimo ženklas (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "galiojimas" #: socialaccount/models.py:175 msgid "social application token" msgstr "socialinės programėlės atpažinimo ženklas" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "socialinės programėlės atpažinimo ženklai" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Neteisingi profilio duomenys" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Prisijungti" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Atšaukti" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Klaidingas atsakymas gaunant užklausos atpažinimo ženklą iš \"%s\". " "Atsakymas buvo: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Klaidingas atsakymas gaunant prieigos atpažinimo ženklą iš \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Nėra užklausos atpažinimo žymės šiam \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Nėra prieigos atpažinimo žymės šiam \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Nėra prieigos prie privataus resurso iš \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Klaidingas atsakymas gaunant užklausos atpažinimo ženklą iš \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Paskyra neaktyvi" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Ši paskyra neaktyvi." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Į %(recipient)s išsiuntėme kodą. Kodo galiojimas greitai pasibaigs, tad " "įrašykite jį kuo greičiau." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Patvirtinti" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Prašyti naujo kodo" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Patvirtinkite Prieigą" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Prašome dar kartą autentifikuotis, kad apsaugotumete savo paskyrą." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Kiti pasirinkimai" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "El. Pašto Patvirtinimas" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Įrašykite Teisingą Patvirtinimo Kodą" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Naudoti kitą el. pašto adresą" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Prisijungti" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Įrašykite Prisijungimo Kodą" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Slaptažodžio atstatymas" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Įveskite slaptažodžio atstatymo kodą" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Telefono patvirtinimas" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Įveskite telefono patvirtinimo kodą" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Naudoti kitą telefono numerį" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "El. pašto adresai" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Šie el. pašto adresas yra susieti su jūsų paskyra:" #: templates/account/email.html:25 msgid "Verified" msgstr "Patvirtintas" #: templates/account/email.html:29 msgid "Unverified" msgstr "Nepatvirtintas" #: templates/account/email.html:34 msgid "Primary" msgstr "Pirminis" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Padaryti pirminiu" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Pakartotinai siųsti patvirtinimą" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Šalinti" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Pridėti el. pašto adresą" #: templates/account/email.html:70 msgid "Add Email" msgstr "Pridėti el. paštą" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Ar tikrai norite ištrinti pasirinktą el. pašto adresą?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Jūs gaunate šį laišką, nes kažkas bandė prisiregistruoti\n" " naudojantis :\n" "\n" "%(email)s\n" "\n" "Paskyra su šiuo el. pašto adresu egzistuoja. Jei užmiršote\n" " apie tai, prašome naudotis užmiršto slaptažodžio procedūra, kad " "atstatytumėte\n" "jūsų paskyrą:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Pakyra Jau Egzistuoja" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Ačiū nuo %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Ačiū, kad naudojate %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "Jūs gaunate šį laišką, nes šie pakitimai buvo padaryti jūsų paskyrai:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Jei neatpažįstate šių pakitimų, imkitės saugumo užtikrinimo veiksmų. " "Paskyros pakitimai buvo įvykdyti iš:\n" "\n" "- IP adreses: %(ip)s\n" "- Naršyklė: %(user_agent)s\n" "- Data: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "" "Jūsų el. pašto adresas buvo pakeistas iš %(from_email)s į %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "El. Paštas Pasikeitė" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Jūsų el. paštas patvirtintas." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "El. Pašto Patvirtinimas" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Jūs gavote šį laišką, kadangi naudotojas %(user_display)s iš %(site_domain)s " "prijungė šį el. pašto adresą prie savo paskyros." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Jūsų el. pašto patvirtinimo kodas yra nurodytas žemiau. Prašome įvesti jį į " "atidarytos naršklės langą." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Patvirtinkite, kad tai yra teisinga, eidami į %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Prašome patvirtinti savo el. pašto adresą" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "El. pašto adresas %(deleted_email)s buvo pašalintas iš jūsų paskyros." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "El. Paštas Pašalintas" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Jūsų el. pašto patvirtinimo kodas yra nurodytas žemiau. Prašome įvesti jį į " "atidarytos naršyklės langą." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "Šis laiškas gali būti ignoruojamas jei šio veiksmo neiniciavote." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Prisijungimo Kodas" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Jūsų slaptažodis pakeistas." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Slaptažodis Pakeistas" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Jūsų slaptažodžio atstatymo kodas yra nurodytas žemiau. Prašome įvesti jį į " "atidarytos naršyklės langą." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Slaptažodžio atstatymo kodas" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Jūs gavote šį laišką, kadangi jūs arba kažkas kitas pateikė slaptažodžio " "keitimo užklausą paskyrai susietai su šiuo el. pašto adresu iš %" "(site_domain)s.\n" "Jei jūs neteikėte slaptažodžio keitimo užklausos, galite ignoruoti šį " "laišką. Kad pakeistumėte savo slaptažodį sekite žemiau esančią nuorodą." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Primename, kad jūsų naudotojo vardas yra %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Slaptažodžio keitimo el. paštas" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Jūsų slaptažodis atstatytas." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Jūsų slaptažodis nustatytas." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Slaptažodžio Nustatymas" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Jūs gaunate šį laišką, nes kažkas bandė prisijungti prie paskyros su el. " "pašto adresu %(email)s, tačiau tokios paskyros duombazėje nėra." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "Jei tai buvote jūs, registruotis galite sekdami nuorodą apačioje." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Nežinoma Paskyra" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "El. Pašto Adresas" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Esamas el. pašto adresas" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Keičiama į" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Jūsų el. pašto adresas dar vis laukia patvirtinimo." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Atšaukti Keitimą" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Pakeista į" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Keisti El. Paštas Adresą" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Patvirtinkite el. pašto adresą" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Prašome patvirtinti, kad %(email)s yra " "%(user_display)s el. pašto adresas." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "%(email)s nepatvirtintas, nes šį el. paštą jau patvirtino kita paskyra." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Šios el. pašto patvirtinimo nuorodos galiojimas baigėsi arba nuoroda yra " "klaidinga. Prašome pateikti naują el. pašto " "patvirtinimo užklausimą." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Jei dar nesusikūrėte paskyros, tuomet prašome pirmiausia " "%(link)ssusikurti%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Prisijunkite su prieigos raktu" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Atsiųskite man prisijungimo kodą" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Atsijungti" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Ar tikrai norite atsijungti?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Negalite ištrinti pirminio (%(email)s) el. pašto adreso." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Patvirtinimo laiškas išsiųstas adresu %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "%(email)s patvirtintas." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "%(email)s el. pašto adresas ištrintas." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Sėkmingai prisijungėte kaip %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Atsijungėte." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Prisijungimo kodas buvo nusiųstas į %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Slaptažodis sėkmingai pakeistas." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Slaptažodis pakeistas sėkmingai." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Patvirtinimo kodas buvo nusiųstas į %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Patvirtinote telefono numerį %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Pirminis el. pašto adresas pakeistas sėkmingai." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Keisti slaptažodį" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Pamiršote slaptažodį?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Pamiršote slaptažodį? Įveskite savo el. pašto adresą žemiau ir mes išsiūsime " "jums laišką, kurio pagalba galėsite pasikeisti slaptažodį." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Atstatyti mano slaptažodį" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "Prašome susisiekti su mumis jei negalite atstatyti slaptažodžio." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Išsiuntėme jums patvirtinimo laišką. Prašome sekite nuorodą pateiktą laiške. " "Susisiekite su mumis jei negausite laiško per kelias minutes." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Klaidinga atpažinimo žymė" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Slaptažodžio atstatymo nuoroda klaidinga, taip gali būti dėl to, kad nuoroda " "jau buvo kartą panaudota. Prašome pakartoti slaptažodžio atstatymą." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Jūsų slaptažodis pakeistas." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Nustatyti slaptažodį" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Keisti telefoną" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Dabartinis telefonas" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Jūsų telefono numeris dar vis laukia patvirtinimo." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Keisti telefoną" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Įrašykite slaptažodį:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Gausite specialų kodą prisijungimui be slaptažodžio." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Prašyti Kodo" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Kiti prisijungimo pasirinkimai" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Registracija" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Registruotis" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Jau turite paskyrą? Prašome %(link)sprisijungti%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Užsiregistruokite naudodami prieigos raktą" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Registracija su prieigos raktu" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Kiti pasirinkimai" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Registracija uždaryta" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Atsiprašome, tačiau registracija šiuo metu yra uždaryta." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Pastaba" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Jūs jau esate prisijungęs, kaip %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Įspėjimas:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Šiuo metu jūs neturite nustatyto el. pašto adreso. Rekomenduojame pridėti " "el. pašto adresą, kad gautumėte pranešimus, galėtumėte atstatyti slaptažodį " "ir pan." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Patvirtinkite savo el. pašto adresą" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Išsiuntėme jums laišką patvirtinimui. Sekite laiške pateiktą nuorodą, kad " "užbaigtumėte registraciją. Jei nematote laiško pagrindiniame skyriuje, " "patikrinkite šlamšto skyrių. Prašome susisiekti su mumis, jei laiško " "negavote per kelias minutes." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Šioje svetainės vietoje privalome gauti iš jūsų patvirtinimą,\n" "kad jūs tikrai esate tas asmuo, kaip teigiate. Dėl šios priežasties\n" "prašome patvirtinti el. pašto adreso nuosavybę. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Išsiuntėme jums patvirtinimo laišką.\n" "Prašome sekite nuorodą pateiktą laiške. Jei laiško nematote, patikrinkite " "šlamšto skyrių. Susisiekite su mumis\n" "jei negausite laiško per kelias minutes." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Pastaba: vis dar galite pakeisti " "savo el. pašto adresą." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Žinutės:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Meniu:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Paskyros ryšiai" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Dviejų Faktorių Autentifikacija" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sesijos" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Autorizuoti" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s nori pasiekti jūsų %(site_name)s paskyrą." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Įveskite įrenginio kodą" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Įveskite kodą, rodomą jūsų įrenginyje." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Tęsti" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Patvirtinkite įrenginį" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Prašome patvirtinti kodą, rodomą jūsų %(client_name)s, kad autorizuotumėte " "šį įrenginį." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Atmesti" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Įrenginys autorizuotas" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Sėkmingai autorizavote savo %(client_name)s įrenginį." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Įrenginys atmestas" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Jūsų %(client_name)s įrenginio autorizacija buvo atmesta." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Klaida" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Likti prisijungus" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Jūsų paskyra apsaugota dviejų veiksnių autentifikavimu. Prašome įvesti " "autentifikatoriaus kodą:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Sugeneruotas naujas dviejų veiksnių autentifikavimo atkūrimo kodų rinkinys." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Sugeneruoti nauji atkūrimo kodai" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Autentifikatoriaus programa aktyvuota." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Autentifikacijos Aplikacija Aktyvuota" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Autentifikatoriaus programa deaktyvuota." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Autentifikacijos Aplikacija Deaktyvuota" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Pridėtas naujas saugumo raktas." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Saugumo raktas pridėtas" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Saugumo raktas buvo pašalintas." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Saugumo raktas pašalintas" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Autentifikatoriaus programa" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Autentifikavimas naudojant autentifikatoriaus programą yra aktyvus." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Autentifikatoriaus programa nėra aktyvi." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Deaktyvuoti" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Aktyvuoti" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Saugumo raktai" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Pridėjote %(count)s saugumo raktą." msgstr[1] "Pridėjote %(count)s saugumo raktus." msgstr[2] "Pridėjote %(count)s saugumo raktų." msgstr[3] "Pridėjote %(count)s saugumo raktų." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Nepridėta jokių saugumo raktų." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Tvarkyti" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Pridėti" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Atkūrimo kodai" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "Yra %(unused_count)s iš %(total_count)s atkūrimo kodų." msgstr[1] "Yra %(unused_count)s iš %(total_count)s atkūrimo kodų." msgstr[2] "Yra %(unused_count)s iš %(total_count)s atkūrimo kodų." msgstr[3] "Yra %(unused_count)s iš %(total_count)s atkūrimo kodų." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Atkūrimo kodai nesukonfigūruoti." #: templates/mfa/index.html:96 msgid "View" msgstr "Peržiūrėti" #: templates/mfa/index.html:102 msgid "Download" msgstr "Atsisiųsti" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Generuoti" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Sugeneruotas naujas atkūrimo kodų rinkinys." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Saugumo raktas pridėtas." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Saugumo raktas pašalintas." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Įveskite autentifikatoriaus kodą:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Ketinate sugeneruoti naują atkūrimo kodų rinkinį savo paskyrai." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Šis veiksmas panaikins jūsų esamus kodus." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Ar tikrai?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Nepanaudoti kodai" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Atsisiųsti kodus" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Generuoti naujus kodus" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Aktyvuoti autentifikatoriaus programą" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Norėdami apsaugoti savo paskyrą dviejų veiksnių autentifikavimu, " "nuskaitykite žemiau esantį QR kodą savo autentifikatoriaus programa. Tada " "įveskite programos sugeneruotą patvirtinimo kodą žemiau." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Autentifikatoriaus paslaptis" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Galite išsaugoti šį slaptą kodą ir naudoti jį autentifikatoriaus programos " "įdiegimui iš naujo vėliau." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Deaktyvuoti autentifikatoriaus programą" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Ketinate deaktyvuoti autentifikavimą, pagrįstą autentifikatoriaus programa. " "Ar tikrai?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Pasitikėti šia naršykle?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Jei pasirinksite pasitikėti šia naršykle, kitą kartą prisijungiant jūsų " "nebus prašoma patvirtinimo kodo." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Pasitikėti %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Nepasitikėti" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Pridėti Saugumo Raktą" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Pašalinti Saugumo Raktą" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Ar tikrai norite pašalinti šį saugumo raktą?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Naudojimas" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Prieigos raktas" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Saugumo raktas" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Šis raktas nenurodo, ar tai yra prieigos raktas." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Nenurodytas" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Pridėta %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Paskutinį kartą naudota %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Redaguoti" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Keisti Saugumo Raktą" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Išsaugoti" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Sukurti prieigos raktą" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Ketinate sukurti prieigos raktą savo paskyrai. Kadangi vėliau galėsite " "pridėti papildomų raktų, galite naudoti aprašomąjį pavadinimą, kad juos " "atskirtumėte." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Sukurti" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Šiai funkcijai reikalingas JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Trečiosios Šalies Prisijungimo Klaida" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "Įvyko nenumatyta klaida bandant prisijungti trčiosios šalies paskyra." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Galite prisijungti prie savo paskyros naudodami vieną iš galimų trečios " "šalies paskyrų:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "" "Šiuo metu jūs neturite nei vienos prijungtos trečiosios šalies paskyros." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Pridėti Trečiosios Šalies paskyrą" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "" "Trečiosios šalies paskyra iš %(provider)s buvo prijungta prie jūsų paskyros." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Trečiosios Šalies Paskyra Prijungta" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" "Trečiosios šalies paskyra iš %(provider)s buvo atjungta nuo jūsų paskyros." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Trečiosios Šalies Paskyra Atjungta" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Prijungti %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "" "Jūs tuojaus prijungsite naują trečiosios šalies paskyrą iš %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Prisijungkite Su %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" "Jūs tuojaus prisijungsite naudojantis trečiosios šalies paskyra iš " "%(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Prisijungimas atšauktas" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Nusprendėte atšaukti prisijungimą naudojant vieną iš esamų paskyrų. Jei tai " "buvo klaida, prašome pakartoti prisijungimą." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Trečiosios šalies paskyra buvo prijungta." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Trečiosios šalies paskyra buvo atjungta." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Jūs beveik prisijungėte prie %(site_name)s naudodami %(provider_name)s\n" "paskyrą. Liko paskutinis žingsnis, užpildyti sekančią formą:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Arba naudokite trečiąją šalį" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Atsijungta nuo visų kitų sesijų." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Pradėta" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP Adresas" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Naršyklė" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Paskutinį kartą matyta" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Esamas" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Atjungti Kitas Sesijas" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Vartotojo Sesijos" #: usersessions/models.py:94 msgid "session key" msgstr "sesijos raktas" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Paskyros ryšiai" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Slaptažodis turi būti sudarytas mažiausiai iš {0} simbolių." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Su jumis sveikinasi %(site_name)s\n" #~ "\n" #~ "Jūs gavote šį laišką, kadangi jūs arba kažkas kitas pateikė slaptažodžio " #~ "keitimo užklausą paskyrai susietai su šiuo el. pašto adresu iš " #~ "%(site_domain)s.\n" #~ "Jei jūs neteikėte slaptažodžio keitimo užklausos, galite ignoruoti šį " #~ "laišką. Kad pakeistumėte savo slaptažodį sekite žemiau esančią nuorodą." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "Šie el. pašto adresas yra susieti su jūsų paskyra:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Patvirtinkite el. pašto adresą" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Prašome prisijungti viena\n" #~ "iš jūsų turimų trečios šalies paskyrų. Arba, susikurkite\n" #~ "naują %(site_name)s paskyrą ir prisijunkite žemiau:" #~ msgid "or" #~ msgstr "arba" #~ msgid "change password" #~ msgstr "keisti slaptažodį" #~ msgid "OpenID Sign In" #~ msgstr "OpenID prisijungimas" #~ msgid "This email address is already associated with another account." #~ msgstr "Šis el. pašto adresas jau susietas su kita paskyra." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Išsiuntėme jums laišką. Prašome susisiekti su mums jei per kelias minutes " #~ "negausite laiško." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "Pateiktas prisijungimo vardas ir/arba slaptažodis yra neteisingi." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "Naudotojo vardui galima naudoti tik raides, skaičius ir @/./+/-/_." #~ msgid "This username is already taken. Please choose another." #~ msgstr "Šis naudotojo vardas jau užimtas. Prašome pasirinkti kitą." #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "Jūs patvirtinote, kad %(email)s yra %" #~ "(user_display)s naudotojo el. pašto adresas." ================================================ FILE: allauth/locale/lv/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2024-04-20 21:51+0200\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : " "2);\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Šis konts šobrīd ir neaktīvs." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Jūs nevarat noņemt savu primāro e-pasta adresi." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Šī e-pasta adrese jau ir piesaistīta šim kontam." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Nepareizs e-pasts un/vai parole." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Norādītais tālruņa numurs un/vai parole nav pareizi." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Lietotājs ar šādu e-pasta adresi jau ir reģistrēts." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Lūdzu ievadiet jūsu šobrīdējo paroli." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Nepareizs kods." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Nepareiza parole." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Nederīga vai novecojusi atslēga." #: account/adapter.py:79 msgid "Invalid login." msgstr "Nederīga pieteikšanās." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Paroles atjaunošanas marķieris bija nederīgs." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Jūs nevarat pievienot vairāk par %d e-pasta adresēm." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Lietotājs ar šādu tālruņa numuru jau ir reģistrēts." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "" "Pārāk daudz neveiksmīgi pieslēgšanās mēģinājumi. Mēģiniet vēlreiz vēlāk." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "E-pasta adrese nav piesaistīta nevienam lietotāja kontam." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Tālruņa numurs nav piesaistīts nevienam lietotāja kontam." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Jūsu primārajai e-pasta adresei jābūt apstiprinātai." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "" "Lietotājvārds nevar tikt izmantots. Lūdzu izvēlietis citu lietotājvārdu." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Nepareizs lietotāja vārds un/vai parole." #: account/adapter.py:98 msgid "Please select only one." msgstr "Lūdzu izvēlieties tikai vienu." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Jaunajai vērtībai jāatšķiras no pašreizējās." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Esiet pacietīgi, jūs sūtāt pārāk daudz pieprasījumu." #: account/adapter.py:826 msgid "Use your password" msgstr "Izmantojiet savu paroli" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Izmantojiet autentifikatora lietotni vai kodu" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Izmantojiet drošības atslēgu" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} ir atzīmēts kā apstiprināts." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Neizdevās atzīmēt {email} kā apstiprinātu." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Atzīmēt izvēlētās e-pasta adreses kā apstiprinātas" #: account/apps.py:11 msgid "Accounts" msgstr "Konti" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-pasts" #: account/fields.py:19 msgid "Email address" msgstr "E-pasta adrese" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Ievadiet tālruņa numuru ar valsts kodu (piem. +1 ASV)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Tālrunis" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Katru reizi jums ir jāievada tā pati parole." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Parole" #: account/forms.py:67 msgid "Remember Me" msgstr "Atcerēties mani" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Lietotājvārds" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Pieteikties" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Lietotājvārds, e-pasts vai tālrunis" #: account/forms.py:117 msgid "Username or email" msgstr "Lietotājvārds vai e-pasts" #: account/forms.py:119 msgid "Username or phone" msgstr "Lietotājvārds vai tālrunis" #: account/forms.py:121 msgid "Email or phone" msgstr "E-pasts vai tālrunis" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Aizmirsāt paroli?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-pasts (vēlreiz)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "E-pasta adreses apstiprināšana" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-pasts (izvēles)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Lietotājvārds (izvēles)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Katru reizi jums ir jāievada tā pati e-pasta adrese." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Parole (vēlreiz)" #: account/forms.py:591 msgid "Current Password" msgstr "Šobrīdējā parole" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Jaunā parole" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Jaunā parole (vēlreiz)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Kods" #: account/models.py:23 msgid "user" msgstr "lietotājs" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "e-pasta adrese" #: account/models.py:31 msgid "verified" msgstr "apstiprināts" #: account/models.py:32 msgid "primary" msgstr "primārā" #: account/models.py:38 msgid "email addresses" msgstr "e-pasta adreses" #: account/models.py:142 msgid "created" msgstr "izveidots" #: account/models.py:143 msgid "sent" msgstr "nosūtīts" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "atslēga" #: account/models.py:149 msgid "email confirmation" msgstr "e-pasta apstiprinājums" #: account/models.py:150 msgid "email confirmations" msgstr "e-pasta apstiprinājumi" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Skatīt savu lietotāja ID" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Skatīt savu e-pasta adresi" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Skatīt savu pamata profila informāciju" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Piešķirt atļaujas" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Aizstājējzīmes nav atļautas, ja vien nav iespējota opcija 'Atļaut URI " "aizstājējzīmes'." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' satur vairāk nekā vienu aizstājējzīmi (*). Katram URI ir atļauta " "tikai viena aizstājējzīme." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Aizstājējzīmes ir atļautas tikai URI resursdatora daļā." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Autorizācijas kods" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Ierīces kods" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Klienta akreditācijas dati" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Atjaunošanas marķieris" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Konfidenciāls" #: idp/oidc/models.py:44 msgid "Public" msgstr "Publisks" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Tvērums(-i), ko klientam ir atļauts pieprasīt. Norādiet vienu vērtību katrā " "rindā, piem.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Gadījumā, ja klients nenorāda tvērumu, tiek izmantoti šie noklusējuma " "tvērumi. Norādiet vienu vērtību katrā rindā, piem.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Atļauto piešķīruma veidu saraksts. Norādiet vienu vērtību katrā rindā, " "piem.: authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Atļauto izcelsmes vietu saraksts starpdomēnu pieprasījumiem, viena katrā " "rindā." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Atļaut aizstājējzīmes (*) pāradresēšanas URI un CORS izcelsmē. Ja iespējots, " "URI var saturēt vienu zvaigznīti, lai atbilstu apakšdomēniem." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Atļauto atbilžu veidu saraksts. Norādiet vienu vērtību katrā rindā, piem.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "klients" #: idp/oidc/models.py:116 msgid "clients" msgstr "klienti" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Jūs nevarat pievienot e-pasta adresi kontam, kas ir aizsargāts ar divfaktoru " "autentifikāciju." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Jūs nevarat deaktivizēt divfaktoru autentifikāciju." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Jūs nevarat ģenerēt atkopšanas kodus bez iespējotas divfaktoru " "autentifikācijas." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Jūs nevarat aktivizēt divfaktoru autentifikāciju, kamēr neesat apstiprinājis " "savu e-pasta adresi." #: mfa/adapter.py:141 msgid "Master key" msgstr "Galvenā atslēga" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Rezerves atslēga" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Atslēga nr. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Atkopšanas kodi" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP autentifikators" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Autentifikatora kods" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Bez paroles" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Bezparoles režīma iespējošana ļauj pieteikties, izmantojot tikai šo atslēgu, " "bet uzliek papildu prasības, piemēram, biometriju vai PIN aizsardzību." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Jau eksistē konts ar šo e-pasta adresi. Lūdzu vispirms ieejiet tajā kontā, " "tad pievienojiet savu %s kontu." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Nederīgs marķieris." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Jūsu kontam nav uzstādīta parole." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Jūsu kontam nav apstiprinātas e-pasta adreses." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Jūs nevarat atvienot savu pēdējo atlikušo trešās puses kontu." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Trešās puses konts jau ir piesaistīts citam kontam." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Sociālie konti" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "sniedzējs" #: socialaccount/models.py:53 msgid "provider ID" msgstr "pakalpojuma sniedzēja ID" #: socialaccount/models.py:57 msgid "name" msgstr "vārds" #: socialaccount/models.py:59 msgid "client id" msgstr "klienta id" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "App ID, vai consumer key" #: socialaccount/models.py:64 msgid "secret key" msgstr "secret key" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API secret, client secret, vai consumer secret" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Key" #: socialaccount/models.py:82 msgid "social application" msgstr "sociālā aplikācija" #: socialaccount/models.py:83 msgid "social applications" msgstr "sociālās aplikācijas" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "pēdējā pieslēgšanās" #: socialaccount/models.py:121 msgid "date joined" msgstr "reģistrācijas datums" #: socialaccount/models.py:122 msgid "extra data" msgstr "papildus informācija" #: socialaccount/models.py:126 msgid "social account" msgstr "sociālais konts" #: socialaccount/models.py:127 msgid "social accounts" msgstr "sociālie konti" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) vai piekļūt marķierim (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "token secret" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) vai atjaunot marķieri (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "beidzas" #: socialaccount/models.py:175 msgid "social application token" msgstr "sociālās aplikācijas marķieris" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "sociālās aplikācijas marķieri" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Nederīgi profila dati" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Pieteikties" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Atcelt" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Nederīga atbilde, iegūstot pieprasījuma marķieri no \"%s\". Atbilde bija: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Nederīga atbilde iegūstot pieejas marķieri no \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Netika saglabāts pieprasījuma marķieris priekš \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Netika saglabāts pieejas marķieris priekš \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Nav pieejas privātam resursam \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Nederīga atbilde iegūstot pieprasījuma marķieri no \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Neaktīvs konts" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Šis konts ir neaktīvs." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Mēs esam nosūtījuši kodu uz %(recipient)s. Koda derīguma termiņš drīz " "beigsies, tāpēc lūdzu ievadiet to drīz." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Apstiprināt" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Pieprasīt jaunu kodu" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Apstiprināt piekļuvi" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Lūdzu autentificējieties atkārtoti, lai aizsargātu savu kontu." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Alternatīvas iespējas" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "E-pasta verifikācija" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Ievadiet e-pasta verifikācijas kodu" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Izmantot citu e-pasta adresi" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Ieiet" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Ievadiet pieteikšanās kodu" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Atjaunot paroli" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Ievadiet paroles atiestatīšanas kodu" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Tālruņa verifikācija" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Ievadiet tālruņa verifikācijas kodu" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Izmantot citu tālruņa numuru" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "E-pasta adreses" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Sekojošas e-pasta adreses ir piesaistītas jūsu kontam:" #: templates/account/email.html:25 msgid "Verified" msgstr "Apstiprināta" #: templates/account/email.html:29 msgid "Unverified" msgstr "Neapstiprināta" #: templates/account/email.html:34 msgid "Primary" msgstr "Primārā" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Iestatīt kā primāro" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Pārsūtīt apstiprināšanu" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Noņemt" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Pievienot e-pasta adresi" #: templates/account/email.html:70 msgid "Add Email" msgstr "Pievienot e-pastu" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Vai jūs tiešām vēlaties noņemt izvēlēto e-pasta adresi?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Jūs saņemat šo e-pastu, jo jūs vai kāds cits mēģināja reģistrēt\n" "kontu, izmantojot e-pasta adresi:\n" "\n" "%(email)s\n" "\n" "Tomēr konts ar šo e-pasta adresi jau pastāv. Gadījumā, ja esat\n" "aizmirsuši par to, lūdzu izmantojiet paroles atjaunošanas procedūru, lai " "atgūtu\n" "savu kontu:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Konts jau pastāv" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Sveiciens no %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Paldies, ka izmantojat %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "Jūs saņemat šo e-pastu, jo jūsu kontā tika veiktas šādas izmaiņas:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Ja jūs neatpazīstat šīs izmaiņas, lūdzu nekavējoties veiciet atbilstošus " "drošības pasākumus. Izmaiņas jūsu kontā radušās no:\n" "\n" "- IP adrese: %(ip)s\n" "- Pārlūkprogramma: %(user_agent)s\n" "- Datums: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Jūsu e-pasts ir nomainīts no %(from_email)s uz %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "E-pasts nomainīts" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Jūsu e-pasts ir apstiprināts." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "E-pasta apstiprināšana" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Jūs saņemat šo e-pasta vēstuli, jo lietotājs %(user_display)s ir norādījis " "jūsu e-pasta adresi, lai reģistrētu kontu vietnē %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Jūsu e-pasta verifikācijas kods ir norādīts zemāk. Lūdzu ievadiet to " "atvērtajā pārlūkprogrammas logā." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Lai apstiprinātu, ka tas ir pareizi, dodieties uz %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Lūdzu apstipriniet savu e-pasta adresi" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "E-pasta adrese %(deleted_email)s ir noņemta no jūsu konta." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "E-pasts noņemts" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Jūsu pieteikšanās kods ir norādīts zemāk. Lūdzu ievadiet to atvērtajā " "pārlūkprogrammas logā." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "Šo e-pastu var droši ignorēt, ja jūs neuzsākāt šo darbību." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Pieteikšanās kods" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Jūsu parole ir nomainīta." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Parole nomainīta" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Jūsu paroles atiestatīšanas kods ir norādīts zemāk. Lūdzu ievadiet to " "atvērtajā pārlūkprogrammas logā." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Paroles atiestatīšanas kods" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Jūs saņemat šo e-pastu, jo jūs vai kāds cits ir pieprasījis paroles " "atiestatīšanu jūsu kontam.\n" "Jūs varat droši ignorēt šo e-pastu, ja jūs nepieprasījāt paroles " "atiestatīšanu. Noklikšķiniet uz zemāk esošās saites, lai atiestatītu paroli." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Gadījumā ja jūs aizmirsāt, tad jūsu lietotājvārds ir %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Paroles atjaunošanas e-pasts" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Jūsu parole ir atiestatīta." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Jūsu parole ir iestatīta." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Parole iestatīta" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Jūs saņemat šo e-pastu, jo jūs vai kāds cits mēģināja piekļūt kontam ar e-" "pastu %(email)s. Tomēr mūsu datubāzē nav ierakstu par šādu kontu." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "Ja tas bijāt jūs, varat reģistrēt kontu, izmantojot zemāk esošo saiti." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Nezināms konts" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "E-pasta adrese" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Pašreizējais e-pasts" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Mainīšana uz" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Jūsu e-pasta adrese vēl gaida verifikāciju." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Atcelt izmaiņas" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Mainīt uz" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Mainīt e-pastu" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Apstipriniet e-pasta adresi" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Lūdzu apstipriniet, ka %(email)s ir e-pasta " "adrese lietotājam %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "Nevar apstiprināt %(email)s, jo tā jau ir apstiprināta ar citu kontu." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Šī e-pasta apstiprināšanas saite ir beigusies vai ir nederīga. Lūdzu pieprasiet jaunu e-pasta apstiprināšanas saiti." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Ja jūs vēl neesat izveidojuši kontu, tad lūdzu " "%(link)spiereģistrējaties%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Pieteikties ar piekļuves atslēgu" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Nosūtiet man pieteikšanās kodu" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Iziet" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Vai jūs tiešām vēlaties iziet?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Jūs nevarat noņemt savu primāro e-pasta adresi (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Apstiprinājuma e-pasts nosūtīts uz %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Jūs esat apstiprinājis %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Noņemta e-pasta adrese %(email)s." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Veiksmīgi iegājuši kā %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Jūs esat izgājuši." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Pieteikšanās kods ir nosūtīts uz %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Parole veiksmīgi nomainīta." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Parole veiksmīgi uzstādīta." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Verifikācijas kods ir nosūtīts uz %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Jūs esat verificējuši tālruņa numuru %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Primārā e-pasta adrese uzstādīta." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Mainīt paroli" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Aizmirsāt paroli?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Aizmirsāt savu paroli? Ievadiet zemāk savu e-pasta adresi, un mēs jums " "nosūtīsim e-pastu, lai to atiestatītu." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Atjaunot manu paroli" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "Lūdzu sazinieties ar mums, ja jums ir kādas problēmas atjaunojot to." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Mēs esam jums nosūtījuši e-pastu. Ja neesat to saņēmuši, lūdzu pārbaudiet " "mēstuļu mapi. Sazinieties ar mums, ja nesaņemat to dažu minūšu laikā." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Nederīgs marķieris" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Paroles atjaunošanas links ir nederīgs, iespējams, tāpēc ka tas jau ir " "izmantots. Lūdzu pieprasiet jaunu paroles " "atjaunošanu." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Jūsu parole ir nomainīta." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Uzstādīt paroli" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Mainīt tālruni" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Pašreizējais tālrunis" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Jūsu tālruņa numurs vēl gaida verifikāciju." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Mainīt tālruni" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Ievadiet savu paroli:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Jūs saņemsiet īpašu kodu pieteikšanai bez paroles." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Pieprasīt kodu" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Citas pieteikšanās iespējas" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Reģistrēties" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Reģistrēties" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Jau ir konts? Tad lūdzu %(link)sspiedied šeit%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Reģistrēties ar piekļuves atslēgu" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Reģistrācija ar piekļuves atslēgu" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Citas iespējas" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Reģistrācija slēgta" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Mēs atvainojamies, bet reģistrācija šobrīd ir slēgta." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Piezīme" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Jūs jau esat ielogojies kā %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Brīdinājums:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Jums pašlaik nav iestatīta neviena e-pasta adrese. Jums patiešām vajadzētu " "pievienot e-pasta adresi, lai varētu saņemt paziņojumus, atiestatīt paroli " "utt." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Apstipriniet savu e-pasta adresi" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Mēs esam nosūtījuši jums verifikācijas e-pastu. Sekojiet norādītajai saitei, " "lai pabeigtu reģistrācijas procesu. Ja neredzat verifikācijas e-pastu " "galvenajā iesūtnē, pārbaudiet mēstuļu mapi. Sazinieties ar mums, ja " "nesaņemat verifikācijas e-pastu dažu minūšu laikā." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Šī vietnes daļa mums nosaka pārbaudīt, ka\n" "jūs esat tas, kas jūs apgalvojat esam. Šim mērķim mēs pieprasām, lai jūs\n" "apstipriniet savu e-pasta adreses piederību. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Mēs esam jums nosūtījuši e-pastu\n" "verifikācijai. Lūdzu noklikšķiniet uz saites šajā e-pastā. Ja neredzat " "verifikācijas e-pastu galvenajā iesūtnē, pārbaudiet mēstuļu mapi. Pretējā " "gadījumā\n" "sazinieties ar mums, ja nesaņemat to dažu minūšu laikā." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Piezīme: jūs joprojām varat nomainīt savu e-pasta adresi." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Ziņojumi:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Izvēlne:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Konta savienojumi" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Divfaktoru autentifikācija" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sesijas" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Autorizēt" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s vēlas piekļūt jūsu %(site_name)s kontam." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Ievadiet ierīces kodu" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Ievadiet kodu, kas tiek rādīts jūsu ierīcē." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Turpināt" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Apstiprināt ierīci" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Lūdzu apstipriniet kodu, kas rādīts jūsu %(client_name)s, lai autorizētu šo " "ierīci." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Noraidīt" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Ierīce autorizēta" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Jūs veiksmīgi autorizējāt savu %(client_name)s ierīci." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Ierīce noraidīta" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Autorizācija jūsu %(client_name)s ierīcei ir noraidīta." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Kļūda" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Palikt pieteicies" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Jūsu konts ir aizsargāts ar divfaktoru autentifikāciju. Lūdzu ievadiet " "autentifikatora kodu:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Jauns divfaktoru autentifikācijas atkopšanas kodu komplekts ir ģenerēts." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Jauni atkopšanas kodi ģenerēti" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Autentifikatora lietotne aktivizēta." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Autentifikatora lietotne aktivizēta" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Autentifikatora lietotne deaktivizēta." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Autentifikatora lietotne deaktivizēta" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Jauna drošības atslēga ir pievienota." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Drošības atslēga pievienota" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Drošības atslēga ir noņemta." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Drošības atslēga noņemta" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Autentifikatora lietotne" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Autentifikācija, izmantojot autentifikatora lietotni, ir aktīva." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Autentifikatora lietotne nav aktīva." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Deaktivizēt" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Aktivizēt" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Drošības atslēgas" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Esat pievienojis %(count)s drošības atslēgu." msgstr[1] "Esat pievienojis %(count)s drošības atslēgas." msgstr[2] "Esat pievienojis %(count)s drošības atslēgu." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Nav pievienota neviena drošības atslēga." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Pārvaldīt" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Pievienot" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Atkopšanas kodi" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "Ir pieejams %(unused_count)s no %(total_count)s atkopšanas kodiem." msgstr[1] "Ir pieejami %(unused_count)s no %(total_count)s atkopšanas kodiem." msgstr[2] "Ir pieejami %(unused_count)s no %(total_count)s atkopšanas kodiem." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Nav iestatīti atkopšanas kodi." #: templates/mfa/index.html:96 msgid "View" msgstr "Skatīt" #: templates/mfa/index.html:102 msgid "Download" msgstr "Lejupielādēt" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Ģenerēt" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Jauns atkopšanas kodu komplekts ir ģenerēts." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Drošības atslēga pievienota." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Drošības atslēga noņemta." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Ievadiet autentifikatora kodu:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Jūs grasāties ģenerēt jaunu atkopšanas kodu komplektu savam kontam." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Šī darbība padarīs jūsu esošos kodus nederīgus." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Vai esat pārliecināti?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Neizmantotie kodi" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Lejupielādēt kodus" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Ģenerēt jaunus kodus" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Aktivizēt autentifikatora lietotni" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Lai aizsargātu savu kontu ar divfaktoru autentifikāciju, noskenējiet zemāk " "esošo QR kodu ar savu autentifikatora lietotni. Pēc tam ievadiet zemāk " "lietotnes ģenerēto verifikācijas kodu." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Autentifikatora noslēpums" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Jūs varat saglabāt šo noslēpumu un izmantot to, lai vēlāk pārinstalētu savu " "autentifikatora lietotni." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Deaktivizēt autentifikatora lietotni" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Jūs grasāties deaktivizēt autentifikāciju ar autentifikatora lietotni. Vai " "esat pārliecināti?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Uzticēties šai pārlūkprogrammai?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Ja izvēlēsieties uzticēties šai pārlūkprogrammai, nākamajā pieteikšanās " "reizē jums netiks prasīts verifikācijas kods." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Uzticēties %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Neuzticēties" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Pievienot drošības atslēgu" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Noņemt drošības atslēgu" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Vai tiešām vēlaties noņemt šo drošības atslēgu?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Lietojums" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Piekļuves atslēga" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Drošības atslēga" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Šī atslēga nenorāda, vai tā ir piekļuves atslēga." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Nenorādīts" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Pievienota %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Pēdējo reizi izmantota %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Rediģēt" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Rediģēt drošības atslēgu" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Saglabāt" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Izveidot piekļuves atslēgu" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Jūs grasāties izveidot piekļuves atslēgu savam kontam. Tā kā vēlāk varat " "pievienot papildu atslēgas, varat izmantot aprakstošu nosaukumu, lai " "atšķirtu atslēgas." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Izveidot" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Šī funkcionalitāte prasa JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Trešās puses pieteikšanās kļūme" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "Notikusi kļūme, mēģinot pieteikties ar jūsu trešās puses kontu." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Jūs varat pieteikties savā kontā, izmantojot jebkuru no šiem trešo pušu " "kontiem:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Jūsu kontam šobrīd nav piesaistīts neviens trešās puses konts." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Pievienot trešās puses kontu" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Trešās puses konts no %(provider)s ir pievienots jūsu kontam." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Trešās puses konts pievienots" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "Trešās puses konts no %(provider)s ir atvienots no jūsu konta." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Trešās puses konts atvienots" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Pievienot %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Jūs grasāties pievienot jaunu trešās puses kontu no %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Pieteikties caur %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" "Jūs grasāties pieteikties, izmantojot trešās puses kontu no %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Ieiešana pārtraukta" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Jūs nolēmāt pārtraukt ieiešanu mūsu lapa izmantojot vienu no jūsu kontiem. " "Ja šī ir kļūda, lūdzu dodaties uz ieiet." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Trešās puses konts ir pievienots." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Trešās puses konts ir atvienots." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Jūs izmantosiet savu %(provider_name)s kontu, lai ieietu\n" "%(site_name)s. Kā pēdējo soli, lūdzu aizpildiet sekojošo formu:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Vai izmantojiet trešo pusi" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Izrakstījāties no visām pārējām sesijām." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Sākta" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP adrese" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Pārlūkprogramma" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Pēdējo reizi redzēts" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Pašreizējā" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Izrakstīties no pārējām sesijām" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Lietotāja sesijas" #: usersessions/models.py:94 msgid "session key" msgstr "sesijas atslēga" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Konta savienojumi" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Parolei jābūt vismaz {0} simbolus garai." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Sveiciens no %(site_name)s!\n" #~ "\n" #~ "Jūs saņemat šo e-pasta vēstuli tāpēc, ka jūs vai kāds cits ir pieprasījis " #~ "paroles atjaunošanu jūsu kontam lapā %(site_domain)s.\n" #~ "Jūs varat droši ignorēt šo e-pasta vēstuli, ja jūs nepieprasījat paroles " #~ "atjaunošanu. Spiedied uz linka zemāk, lai atjaunotu jūsu paroli." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "Sekojošas e-pasta adreses ir piesaistītas jūsu kontam:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Apstipriniet e-pasta adresi" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Lūdzu ieejiet iekšā ar vienu\n" #~ "no jūsu eksistējošiem trešās puses kontiem. Vai reģistrējieties\n" #~ "%(site_name)s kontam un ieejiet zemāk:" #~ msgid "or" #~ msgstr "vai" #~ msgid "change password" #~ msgstr "Nomainīt paroli" #~ msgid "OpenID Sign In" #~ msgstr "Ieiet ar OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "Šī e-pasta adrese jau ir piesaistīta citam kontam." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Mēs jums nosūtījām e-pasta vēstuli. Lūdzu sazinieties ar mums, ja dažu " #~ "minūšu laikā nesaņemat vēstuli." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "Nepareiza pieteikšanās informācija un/vai parole." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "Lietotājvārds var saturēt tikai burtus, ciparus un @/./+/-/_." #~ msgid "This username is already taken. Please choose another." #~ msgstr "Šis lietotājvārds jau ir aizņemts. Lūdzu izvēlaties citu." ================================================ FILE: allauth/locale/mn/LC_MESSAGES/django.po ================================================ # Mongolian Translation # Copyright (C) 2021 # This file is distributed under the same license as the PACKAGE package. # Bilgutei Erdenebayar , 2021. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2024-04-20 21:32+0200\n" "Last-Translator: Bilgutei Erdenebayar \n" "Language-Team: Mongolian \n" "Language: mn\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Энэ бүртгэл одоогоор идэвхгүй байна." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Та үндсэн имэйл хаягаа устгах боломжгүй." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Энэ имэйл хаяг энэ бүртгэлтэй холбогдсон байна." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Таны оруулсан имэйл хаяг болон/эсвэл нууц үг буруу байна." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Таны оруулсан утасны дугаар болон/эсвэл нууц үг буруу байна." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Өөр хэрэглэгч энэ имэйл хаягаар бүртгүүлсэн байна." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Одоогийн нууц үгээ оруулна уу." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Код буруу байна." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Нууц үг буруу байна." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Түлхүүр буруу эсвэл хугацаа нь дууссан." #: account/adapter.py:79 msgid "Invalid login." msgstr "Нэвтрэлт буруу байна." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Нууц үг шинэчлэх токен буруу байна." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Та %d-с илүү имэйл хаяг нэмэх боломжгүй." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Өөр хэрэглэгч энэ утасны дугаараар бүртгүүлсэн байна." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Хэт олон амжилтгүй нэвтрэх оролдлого. Дараа дахин оролдоорой." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Имэйл хаяг ямар ч хэрэглэгчийн бүртгэлтэй холбогдоогүй." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Утасны дугаар ямар ч хэрэглэгчийн бүртгэлтэй холбогдоогүй." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Таны үндсэн имэйл хаягийг баталгаажуулсан байх ёстой." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Хэрэглэгчийн нэрийг ашиглах боломжгүй. Өөр нэр сонгоно уу." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Таны оруулсан имэйл хаяг болон/эсвэл нууц үг буруу байна." #: account/adapter.py:98 msgid "Please select only one." msgstr "Зөвхөн нэгийг сонгоно уу." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Шинэ утга одоогийнхоос ялгаатай байх ёстой." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Тэвчээртэй байна уу, та хэт олон хүсэлт илгээж байна." #: account/adapter.py:826 msgid "Use your password" msgstr "Нууц үгээ ашиглах" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Баталгаажуулагч апп эсвэл код ашиглах" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Аюулгүй байдлын түлхүүр ашиглах" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} баталгаажсан гэж тэмдэглэгдлээ." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "{email}-г баталгаажсан гэж тэмдэглэхэд алдаа гарлаа." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Сонгосон имэйл хаягуудыг баталгаажсан гэж тэмдэглэх" #: account/apps.py:11 msgid "Accounts" msgstr "Бүртгэлүүд" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "Имэйл хаяг" #: account/fields.py:19 msgid "Email address" msgstr "Имэйл хаяг" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Улсын кодыг оруулсан утасны дугаар оруулна уу (жнь: АНУ-д +1)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Утас" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Та өмнө оруулсантай ижил нууц үг оруулах ёстой." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Нууц үг" #: account/forms.py:67 msgid "Remember Me" msgstr "Намайг Санах" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Хэрэглэгчийн нэр" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Нэвтрэх" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Хэрэглэгчийн нэр, имэйл эсвэл утас" #: account/forms.py:117 msgid "Username or email" msgstr "Хэрэглэгчийн нэр эсвэл имэйл" #: account/forms.py:119 msgid "Username or phone" msgstr "Хэрэглэгчийн нэр эсвэл утас" #: account/forms.py:121 msgid "Email or phone" msgstr "Имэйл эсвэл утас" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Нууц үгээ мартсан уу?" #: account/forms.py:287 msgid "Email (again)" msgstr "Имэйл (дахин)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Имэйл хаягийн баталгаажуулалт" #: account/forms.py:302 msgid "Email (optional)" msgstr "Имэйл (заавал биш)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Хэрэглэгчийн нэр (заавал биш)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Та өмнө оруулсантай ижил имэйл бичих ёстой." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Нууц үг (дахин)" #: account/forms.py:591 msgid "Current Password" msgstr "Одоогын Нууц Үг" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Шинэ Нууц Үг" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Шинэ Нууц Үг (дахин)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Код" #: account/models.py:23 msgid "user" msgstr "хэрэглэгч" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "имэйл хаяг" #: account/models.py:31 msgid "verified" msgstr "баталгаажуулсан" #: account/models.py:32 msgid "primary" msgstr "үндсэн" #: account/models.py:38 msgid "email addresses" msgstr "имэйл хаягууд" #: account/models.py:142 msgid "created" msgstr "үүсгэсэн" #: account/models.py:143 msgid "sent" msgstr "илгээсэн" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "түлхүүр" #: account/models.py:149 msgid "email confirmation" msgstr "имэйл баталгаажуулалт" #: account/models.py:150 msgid "email confirmations" msgstr "имэйл баталгаажуулалт" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Хэрэглэгчийн ID-гаа харах" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Имэйл хаягаа харах" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Үндсэн профайл мэдээллээ харах" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Зөвшөөрөл олгох" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "'URI орлуулагч тэмдэгтийг зөвшөөрөх' идэвхжүүлээгүй бол орлуулагч тэмдэгт " "(*) зөвшөөрөгдөхгүй." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' нэгээс олон орлуулагч тэмдэгт (*) агуулж байна. Нэг URI-д зөвхөн " "нэг орлуулагч тэмдэгт зөвшөөрөгдөнө." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Орлуулагч тэмдэгт зөвхөн URI-ийн хост нэрийн хэсэгт зөвшөөрөгдөнө." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Зөвшөөрлийн код" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Төхөөрөмжийн код" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Клиентийн итгэмжлэл" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Шинэчлэх токен" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Нууцлалтай" #: idp/oidc/models.py:44 msgid "Public" msgstr "Нийтийн" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Клиент хүсэлт гаргах боломжтой хүрээ. Мөр бүрт нэг утга оруулна уу, жнь: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Хэрэв клиент ямар ч хүрээ заагаагүй бол эдгээр анхдагч хүрээг ашиглана. Мөр " "бүрт нэг утга оруулна уу, жнь: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Зөвшөөрөгдсөн олголтын төрлүүдийн жагсаалт. Мөр бүрт нэг утга оруулна уу, " "жнь: authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Хөндлөн домэйн хүсэлтийн зөвшөөрөгдсөн эх үүсвэрүүдийн жагсаалт, мөр бүрт " "нэг." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "URI чиглүүлэлт болон CORS эх үүсвэрт орлуулагч тэмдэгт (*) зөвшөөрөх. " "Идэвхжүүлсэн үед URI-д дэд домэйнтэй таарахын тулд нэг од агуулж болно." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Зөвшөөрөгдсөн хариу төрлүүдийн жагсаалт. Мөр бүрт нэг утга оруулна уу, жнь: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "клиент" #: idp/oidc/models.py:116 msgid "clients" msgstr "клиентүүд" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Хоёр хүчин зүйлийн баталгаажуулалтаар хамгаалагдсан бүртгэлд имэйл хаяг " "нэмэх боломжгүй." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Хоёр хүчин зүйлийн баталгаажуулалтыг идэвхгүй болгох боломжгүй." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Хоёр хүчин зүйлийн баталгаажуулалт идэвхжүүлээгүйгээр сэргээх код үүсгэх " "боломжгүй." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Имэйл хаягаа баталгаажуулаагүй бол хоёр хүчин зүйлийн баталгаажуулалтыг " "идэвхжүүлэх боломжгүй." #: mfa/adapter.py:141 msgid "Master key" msgstr "Мастер түлхүүр" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Нөөц түлхүүр" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Түлхүүр №{number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Сэргээх кодууд" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP Баталгаажуулагч" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Баталгаажуулагч код" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Нууц үггүй" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Нууц үггүй горимыг идэвхжүүлснээр зөвхөн энэ түлхүүрээр нэвтрэх боломжтой " "боловч биометрик эсвэл PIN хамгаалалт зэрэг нэмэлт шаардлагуудыг тавина." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Энэ имэйл хаягтай бүртгэл аль хэдийн байна. Эхлээд тэр бүртгэлд нэвтэрч, " "дараа нь %s бүртгэлээ холбоно уу." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Токен буруу байна." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Таны бүртгэлд нууц үг тохируулаагүй байна." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Таны бүртгэлд баталгаажсан имэйл хаяг алга." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Та сүүлчийн үлдсэн гуравдагч этгээдийн бүртгэлээ салгах боломжгүй." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Гуравдагч этгээдийн бүртгэл аль хэдийн өөр бүртгэлд холбогдсон байна." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Сошиал Бүртгэлүүд" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "үйлчилгээ үзүүлэгч" #: socialaccount/models.py:53 msgid "provider ID" msgstr "үйлчилгээ үзүүлэгчийн ID" #: socialaccount/models.py:57 msgid "name" msgstr "нэр" #: socialaccount/models.py:59 msgid "client id" msgstr "клиент ID" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "Апп ID эсвэл хэрэглэгчийн түлхүүр" #: socialaccount/models.py:64 msgid "secret key" msgstr "нууц түлхүүр" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API нууц, клиент нууц эсвэл хэрэглэгчийн нууц" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Түлхүүр" #: socialaccount/models.py:82 msgid "social application" msgstr "сошиал апп" #: socialaccount/models.py:83 msgid "social applications" msgstr "сошиал апп-ууд" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "сүүлд нэвтэрсэн" #: socialaccount/models.py:121 msgid "date joined" msgstr "бүртгүүлсэн огноо" #: socialaccount/models.py:122 msgid "extra data" msgstr "нэмэлт өгөгдөл" #: socialaccount/models.py:126 msgid "social account" msgstr "сошиал хаяг" #: socialaccount/models.py:127 msgid "social accounts" msgstr "сошиал хаягууд" #: socialaccount/models.py:161 msgid "token" msgstr "токен" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) эсвэл нэвтрэх токен (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "токен нууц" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) эсвэл шинэчлэх токен (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "дуусах хугацаа" #: socialaccount/models.py:175 msgid "social application token" msgstr "сошиал апп токен" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "сошиал апп токенууд" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Профайл мэдээлэл буруу байна" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Нэвтрэх" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Болих" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "\"%s\"-с request токен авах үед буруу хариу ирлээ. Хариу: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "\"%s\"-с access токен авах үед буруу хариу." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "\"%s\"-д request токен хадгалагдаагүй." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "\"%s\"-д access токен хадгалагдаагүй." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "\"%s\" дээрх private resource-д хандах боломжгүй." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "\"%s\"-с request токен авах үед буруу хариу." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Бүртгэл идэвхгүй" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Энэ бүртгэл идэвхгүй байна." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Бид %(recipient)s руу код илгээлээ. Кодын хугацаа удахгүй дуусах тул хурдан " "оруулна уу." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Баталгаажуулах" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Шинэ код хүсэх" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Хандалтыг баталгаажуулах" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Бүртгэлээ хамгаалахын тулд дахин баталгаажуулалт хийнэ үү." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Өөр сонголтууд" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Имэйл баталгаажуулалт" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Имэйл баталгаажуулалтын код оруулна уу" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Өөр имэйл хаяг ашиглах" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Нэвтрэх" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Нэвтрэх код оруулна уу" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Нууц үг шинэчлэх" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Нууц үг шинэчлэх код оруулна уу" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Утасны баталгаажуулалт" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Утасны баталгаажуулалтын код оруулна уу" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Өөр утасны дугаар ашиглах" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Имэйл Хаягууд" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Дараах и-мэйл хаягууд таны бүртгэлтэй холбоотой байна:" #: templates/account/email.html:25 msgid "Verified" msgstr "Баталгаажсан" #: templates/account/email.html:29 msgid "Unverified" msgstr "Баталгаажаагүй" #: templates/account/email.html:34 msgid "Primary" msgstr "Үндсэн" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Үндсэн Болгох" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Баталгаажуулалтыг дахин илгээх" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Устгах" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Имэйл хаяг нэмэх" #: templates/account/email.html:70 msgid "Add Email" msgstr "Имэйл нэмэх" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Та үнэхээр сонгосон имэйл хаягаа устгахыг хүсэж байна уу?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Та эсвэл өөр хэн нэгэн дараах имэйл хаягаар\n" "бүртгэл үүсгэхийг оролдсон тул энэ имэйлийг хүлээн авч байна:\n" "\n" "%(email)s\n" "\n" "Гэвч энэ имэйл хаягтай бүртгэл аль хэдийн байна. Хэрэв та үүнийг\n" "мартсан бол нууц үг сэргээх процедурыг ашиглан\n" "бүртгэлээ сэргээнэ үү:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Бүртгэл аль хэдийн байна" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "%(site_name)s-с мэнд хүргэж байна!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "%(site_name)s-г хэрэглэсэнд баярлалаа!%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Таны бүртгэлд дараах өөрчлөлт хийгдсэн тул энэ имэйлийг хүлээн авч байна:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Хэрэв та энэ өөрчлөлтийг таниагүй бол нэн даруй зохих аюулгүй байдлын арга " "хэмжээ авна уу. Таны бүртгэлийн өөрчлөлт дараахаас ирсэн:\n" "\n" "- IP хаяг: %(ip)s\n" "- Хөтөч: %(user_agent)s\n" "- Огноо: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Таны имэйл %(from_email)s-с %(to_email)s руу өөрчлөгдлөө." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Имэйл өөрчлөгдсөн" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Таны имэйл баталгаажлаа." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Имэйл баталгаажуулалт" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "%(user_display)s хэрэглэгч %(site_domain)s дээр бүртгэл үүсгэхийн тулд таны " "имэйл хаягийг оруулсан тул та энэ имэйлийг хүлээн авч байна." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Таны имэйл баталгаажуулах код доор байна. Нээлттэй хөтчийн цонхонд оруулна " "уу." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Зөв эсэхийг баталгаажуулахын тулд %(activate_url)s руу очно уу" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Имэйл хаягаа баталгаажуулна уу" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "%(deleted_email)s имэйл хаяг таны бүртгэлээс устгагдлаа." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Имэйл устгагдсан" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "Таны нэвтрэх код доор байна. Нээлттэй хөтчийн цонхонд оруулна уу." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "Хэрэв та энэ үйлдлийг хийгээгүй бол энэ имэйлийг үл тоомсорлож болно." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Нэвтрэх код" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Таны нууц үг өөрчлөгдлөө." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Нууц үг өөрчлөгдсөн" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Таны нууц үг шинэчлэх код доор байна. Нээлттэй хөтчийн цонхонд оруулна уу." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Нууц үг шинэчлэх код" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Та эсвэл өөр хэн нэгэн таны бүртгэлийн нууц үг шинэчлэх хүсэлт гаргасан тул " "энэ имэйлийг хүлээн авч байна.\n" "Хэрэв та нууц үг шинэчлэх хүсэлт гаргаагүй бол энэ имэйлийг үл тоомсорлож " "болно. Нууц үгээ шинэчлэхийн тулд доорх холбоос дээр дарна уу." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Хэрэв та мартсан бол таны хэрэглэгчийн нэр %(username)s байна." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Нууц Үг Шинэчлэх Имэйл" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Таны нууц үг шинэчлэгдлээ." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Таны нууц үг тохируулагдлаа." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Нууц үг тохируулагдсан" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Та эсвэл өөр хэн нэгэн %(email)s имэйлтэй бүртгэлд хандахыг оролдсон тул энэ " "имэйлийг хүлээн авч байна. Гэвч бидний мэдээллийн санд ийм бүртгэл " "бүртгэгдээгүй байна." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "Хэрэв энэ нь та байсан бол доорх холбоосыг ашиглан бүртгүүлж болно." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Тодорхойгүй бүртгэл" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Имэйл хаяг" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Одоогийн имэйл" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Солигдож байна" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Таны имэйл хаяг баталгаажуулалт хүлээж байна." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Өөрчлөлтийг болих" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Солих" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Имэйл солих" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Имэйл Хаяг Баталгаажуулах" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "%(email)s нь %(user_display)s хэрэглэгчийн " "имэйл хаяг гэдгийг баталгаажуулна уу." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "%(email)s-г баталгаажуулах боломжгүй, учир нь өөр бүртгэл аль хэдийн " "баталгаажуулсан байна." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Энэ имэйл баталгаажуулах холбоосын хугацаа дууссан эсвэл хүчингүй байна. Шинэ имэйл баталгаажуулах хүсэлт гаргана уу." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Хэрэв та бүртгэл үүсгээгүй байгаа бол эхлээд %(link)sбүртгүүлнэ " "үү%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Нэвтрэх түлхүүрээр нэвтрэх" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Надад нэвтрэх код илгээх" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Гарах" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Та холболт салгахдаа итгэлтэй байна уу?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Та үндсэн имэйл хаяг (%(email)s)-г устгах боломжгүй." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "%(email)s-руу баталгаажуулах имэйл илгээсэн." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Та %(email)s-г баталгаажууллаа." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "%(email)s и-мэйл хаягийг устгасан." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "%(name)s нэрээр амжилттай нэвтэрлээ." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Та холболт салгалаа." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Нэвтрэх код %(recipient)s руу илгээгдлээ." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Нууц үг амжилттай өөрчлөгдлөө." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Нууц үгийг амжилттай тохирууллаа." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Баталгаажуулах код %(phone)s руу илгээгдлээ." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Та %(phone)s утасны дугаарыг баталгаажууллаа." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Үндсэн имэйл хаягийг тохируулсан." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Нууц үг солих" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Нууц Үг Мартсан?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Нууц үгээ мартсан уу? Доор имэйл хаягаа оруулбал бид танд нууц үг шинэчлэх " "имэйл илгээнэ." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Нууц Үг Шинэчлэх" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "Нууц үгээ шинэчлэхэд асуудал гарвал бидэнтэй холбогдоно уу." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Бид танд имэйл илгээлээ. Хэрэв хүлээн аваагүй бол спам хавтсаа шалгана уу. " "Хэдэн минутын дотор хүлээн аваагүй бол бидэнтэй холбоо барина уу." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Муу токен" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Нууц үг шинэчлэх холбоос хүчингүй байсан, үүнийг аль хэдийн ашигласан байж " "магадгүй. шинэ нууц үг шинэчлэх хүсэлт " "гаргана уу." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Таны нууц үг одоо өөрчлөгдсөн байна." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Нууц үг тохируулах" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Утас солих" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Одоогийн утас" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Таны утасны дугаар баталгаажуулалт хүлээж байна." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Утас солих" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Нууц үгээ оруулна уу:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Та нууц үггүй нэвтрэхийн тулд тусгай код хүлээн авна." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Код хүсэх" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Бусад нэвтрэх сонголтууд" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Бүртгүүлэх" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Бүртгүүлэх" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "" "Бүртгэлтэй юу? Тэгвэл бүртгэлтэй хаягаар %(link)sнэвтрэнэ үү%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Нэвтрэх түлхүүрээр бүртгүүлэх" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Нэвтрэх түлхүүрээр бүртгүүлэх" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Бусад сонголтууд" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Бүртгэл хаалттай" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Уучлаарай, бүртгэл одоогоор хаалттай байна." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Анхаарна уу" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Та аль хэдийн %(user_display)s нэрээр нэвтэрсэн байна." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Анхааруулга:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Танд одоогоор тохируулсан имэйл хаяг байхгүй байна. Мэдэгдэл хүлээн авах, " "нууц үгээ шинэчлэх зэрэгт имэйл хаяг нэмэх хэрэгтэй." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Имэйл хаягаа баталгаажуулна уу" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Бид танд баталгаажуулах имэйл илгээлээ. Бүртгэлийг дуусгахын тулд өгөгдсөн " "холбоосыг дагана уу. Баталгаажуулах имэйл ирсэн эсэхийг харахгүй бол спам " "хавтсаа шалгана уу. Хэдэн минутын дотор хүлээн аваагүй бол бидэнтэй холбоо " "барина уу." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Сайтын энэ хэсэг нь таныг өөрийгөө мөн болохыг\n" "баталгаажуулахыг биднээс шаарддаг. Энэ зорилгоор бид таныг\n" "имэйл хаягийнхаа эзэмшигч мөн эсэхийг баталгаажуулахыг шаарддаг. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Бид танд баталгаажуулах имэйл илгээлээ.\n" "Тэр имэйл дэх холбоос дээр дарна уу. Баталгаажуулах имэйл ирсэн эсэхийг " "харахгүй бол спам хавтсаа шалгана уу. Эсвэл\n" "хэдэн минутын дотор хүлээн аваагүй бол бидэнтэй холбоо барина уу." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Анхааруулга: та имэйл хаягаа " "өөрчлөх боломжтой хэвээр байна." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Мэдэгдлүүд:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Цэс:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Бүртгэлийн холболтууд" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Хоёр хүчин зүйлийн баталгаажуулалт" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Сессионууд" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Зөвшөөрөх" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s таны %(site_name)s бүртгэлд хандахыг хүсч байна." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Төхөөрөмжийн код оруулна уу" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Төхөөрөмж дээрээ харагдаж буй кодыг оруулна уу." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Үргэлжлүүлэх" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Төхөөрөмж баталгаажуулах" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Энэ төхөөрөмжийг зөвшөөрөхийн тулд %(client_name)s дээр харуулсан кодыг " "баталгаажуулна уу." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Татгалзах" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Төхөөрөмж зөвшөөрөгдсөн" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Та %(client_name)s төхөөрөмжөө амжилттай зөвшөөрлөө." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Төхөөрөмж татгалзагдсан" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Таны %(client_name)s төхөөрөмжийн зөвшөөрөл татгалзагдлаа." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Алдаа" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Нэвтэрсэн хэвээр байх" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Таны бүртгэл хоёр хүчин зүйлийн баталгаажуулалтаар хамгаалагдсан байна. " "Баталгаажуулагч код оруулна уу:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "Хоёр хүчин зүйлийн баталгаажуулалтын шинэ сэргээх кодууд үүсгэгдлээ." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Шинэ сэргээх кодууд үүсгэгдсэн" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Баталгаажуулагч апп идэвхжлээ." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Баталгаажуулагч апп идэвхжсэн" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Баталгаажуулагч апп идэвхгүй боллоо." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Баталгаажуулагч апп идэвхгүй болсон" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Шинэ аюулгүй байдлын түлхүүр нэмэгдлээ." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Аюулгүй байдлын түлхүүр нэмэгдсэн" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Аюулгүй байдлын түлхүүр устгагдлаа." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Аюулгүй байдлын түлхүүр устгагдсан" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Баталгаажуулагч апп" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Баталгаажуулагч апп ашиглан баталгаажуулалт идэвхтэй байна." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Баталгаажуулагч апп идэвхгүй байна." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Идэвхгүй болгох" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Идэвхжүүлэх" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Аюулгүй байдлын түлхүүрүүд" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "%(count)s аюулгүй байдлын түлхүүр нэмлээ." msgstr[1] "%(count)s аюулгүй байдлын түлхүүр нэмлээ." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Аюулгүй байдлын түлхүүр нэмэгдээгүй байна." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Удирдах" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Нэмэх" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Сэргээх кодууд" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "%(total_count)s сэргээх кодоос %(unused_count)s боломжтой байна." msgstr[1] "%(total_count)s сэргээх кодоос %(unused_count)s боломжтой байна." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Сэргээх код тохируулагдаагүй байна." #: templates/mfa/index.html:96 msgid "View" msgstr "Харах" #: templates/mfa/index.html:102 msgid "Download" msgstr "Татах" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Үүсгэх" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Шинэ сэргээх кодууд үүсгэгдлээ." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Аюулгүй байдлын түлхүүр нэмэгдлээ." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Аюулгүй байдлын түлхүүр устгагдлаа." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Баталгаажуулагч код оруулна уу:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Та бүртгэлийнхээ шинэ сэргээх кодуудыг үүсгэх гэж байна." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Энэ үйлдэл таны одоогийн кодуудыг хүчингүй болгоно." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Та итгэлтэй байна уу?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Ашиглаагүй кодууд" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Кодууд татах" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Шинэ кодууд үүсгэх" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Баталгаажуулагч апп идэвхжүүлэх" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Бүртгэлээ хоёр хүчин зүйлийн баталгаажуулалтаар хамгаалахын тулд доорх QR " "кодыг баталгаажуулагч аппаараа уншуулна уу. Дараа нь аппаас үүсгэсэн " "баталгаажуулах кодыг доор оруулна уу." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Баталгаажуулагчийн нууц" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Та энэ нууцыг хадгалж, дараа нь баталгаажуулагч аппаа дахин суулгахад " "ашиглаж болно." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Баталгаажуулагч апп идэвхгүй болгох" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Та баталгаажуулагч апп дээр суурилсан баталгаажуулалтыг идэвхгүй болгох гэж " "байна. Та итгэлтэй байна уу?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Энэ хөтөчид итгэх үү?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Хэрэв та энэ хөтөчид итгэхээр сонговол дараагийн удаа нэвтрэхэд " "баталгаажуулах код асуухгүй." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "%(period)s итгэх" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Итгэхгүй" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Аюулгүй байдлын түлхүүр нэмэх" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Аюулгүй байдлын түлхүүр устгах" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Та энэ аюулгүй байдлын түлхүүрийг устгахдаа итгэлтэй байна уу?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Хэрэглээ" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Нэвтрэх түлхүүр" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Аюулгүй байдлын түлхүүр" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Энэ түлхүүр нэвтрэх түлхүүр мөн эсэхийг заагаагүй байна." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Тодорхойгүй" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "%(created_at)s-д нэмэгдсэн" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Сүүлд ашигласан %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Засах" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Аюулгүй байдлын түлхүүр засах" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Хадгалах" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Нэвтрэх түлхүүр үүсгэх" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Та бүртгэлдээ нэвтрэх түлхүүр үүсгэх гэж байна. Дараа нь нэмэлт түлхүүрүүд " "нэмэх боломжтой тул тэдгээрийг ялгахын тулд тодорхойлох нэр ашиглаж болно." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Үүсгэх" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Энэ функц JavaScript шаарддаг." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Гуравдагч этгээдээр нэвтрэхэд алдаа гарлаа" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Таны гуравдагч этгээдийн бүртгэлээр нэвтрэхийг оролдох үед алдаа гарлаа." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Та дараах гуравдагч этгээдийн бүртгэлүүдийн аль нэгийг ашиглан өөрийн " "бүртгэлд нэвтэрч болно:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "" "Танд одоогоор энэ бүртгэлд холбогдсон гуравдагч этгээдийн бүртгэл байхгүй " "байна." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Гуравдагч этгээдийн бүртгэл нэмэх" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "%(provider)s-ийн гуравдагч этгээдийн бүртгэл таны бүртгэлд холбогдлоо." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Гуравдагч этгээдийн бүртгэл холбогдсон" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" "%(provider)s-ийн гуравдагч этгээдийн бүртгэл таны бүртгэлээс салгагдлаа." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Гуравдагч этгээдийн бүртгэл салгагдсан" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "%(provider)s холбох" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "" "Та %(provider)s-ийн шинэ гуравдагч этгээдийн бүртгэлийг холбох гэж байна." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "%(provider)s-р нэвтрэх" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "Та %(provider)s-ийн гуравдагч этгээдийн бүртгэлээр нэвтрэх гэж байна." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Нэвтрэлт цуцлагдсан" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Та одоо байгаа бүртгэлийнхээ аль нэгийг ашиглан манай сайтад нэвтрэхээ " "цуцлахаар шийдсэн. Хэрэв энэ нь алдаа байсан бол нэвтэрнэ үү." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Гуравдагч этгээдийн бүртгэл холбогдлоо." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Гуравдагч этгээдийн бүртгэл салгагдлаа." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Та %(site_name)s руу нэвтрэхийн тулд %(provider_name)s бүртгэлээ\n" "ашиглах гэж байна. Эцсийн алхам болгон дараах маягтыг бөглөнө үү:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Эсвэл гуравдагч этгээд ашиглах" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Бусад бүх сессионоос гарлаа." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Эхэлсэн" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP хаяг" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Хөтөч" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Сүүлд харагдсан" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Одоогийн" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Бусад сессионоос гарах" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Хэрэглэгчийн сессионууд" #: usersessions/models.py:94 msgid "session key" msgstr "сессионы түлхүүр" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Бүртгэлийн холболтууд" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Нууц үг хамгийн багадаа {0} тэмдэгттэй байх ёстой." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Та эсвэл өөр хэн нэгэн таны бүртгэлтэй нууц үгийг хүссэн тул нэ " #~ "имэйлийгхүлээн авч байна.\n" #~ "Хэрэв та нууц үг шинэчлэх хүсэлт гаргаагүй бол энэ имэйлийг устгаж " #~ "болно.Нууц үгээ шинэчлэх бол доорх холбоос дээр дарна уу." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "Дараах и-мэйл хаягууд таны бүртгэлтэй холбоотой байна:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Имэйл Хаяг Баталгаажуулах" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Одоо байгаа гуравдагч\n" #~ "этгээдийн бүртгэлийн аль нэгээр нэвтэрнэ үү. Эсвэл %(site_name)s бүртгэлд " #~ "%(link)sбүртгүүлж%(end_link)sдоор нэвтэрнэ үү:" #~ msgid "or" #~ msgstr "эсвэл" #~ msgid "change password" #~ msgstr "нууц үг солих" #~ msgid "OpenID Sign In" #~ msgstr "OpenID-р Нэвтрэх" #~ msgid "This email address is already associated with another account." #~ msgstr "Энэ имэйл хаяг өөр бүртгэлтэй холбогдсон байна." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Бид танд имэйл илгээсэн. Хэдхэн минутын дотор хүлээж авахгүй бол бидэнтэй " #~ "холбоо барина уу." ================================================ FILE: allauth/locale/nb/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Free Software Foundation, Inc. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-06-15 19:17+0000\n" "Last-Translator: Anders Birkenes \n" "Language-Team: Norwegian Bokmål \n" "Language: nb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.12-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Denne kontoen er inaktiv." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Du kan ikke fjerne den primære e-postadressen." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Denne e-postadressen er allerede tilknyttet denne kontoen." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "E-postadressen og/eller passordet du oppgav er feil." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Telefonnummeret og/eller passordet du oppgav er feil." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "En bruker med denne e-postadressen er allerede registrert." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Vennligst skriv inn ditt passord." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Feil kode." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Feil passord." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Ugyldig eller utgått token." #: account/adapter.py:79 msgid "Invalid login." msgstr "Ugyldig pålogging." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Nøkkelen for tilbakestilling av passord var ugyldig." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Du kan ikke legge til mer enn %d epostadresser." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "En bruker med dette telefonnummeret er allerede registrert." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "For mange innloggingsforsøk. Vennligst prøv igjen senere." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Epostadressen er ikke tilknyttet noen brukerkonto." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Telefonnummeret er ikke tilknyttet noen brukerkonto." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Din primære e-postadresse må være verifisert." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Brukernavnet kan ikke benyttes. Vennligst velg et annet brukernavn." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Brukernavnet og/eller passordet du oppgav er feil." #: account/adapter.py:98 msgid "Please select only one." msgstr "Velg kun en." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Den nye verdien må være forskjellig fra den nåværende." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Vær tålmodig, du sender for mange forespørsler." #: account/adapter.py:826 msgid "Use your password" msgstr "Bruk passordet ditt" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Bruk autentiseringsapp eller kode" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Bruk en sikkerhetsnøkkel" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "Markerte {email} som verifisert." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Kunne ikke markere {email} som verifisert." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Marker den valgte epostadressen som bekreftet" #: account/apps.py:11 msgid "Accounts" msgstr "Kontoer" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-post" #: account/fields.py:19 msgid "Email address" msgstr "E-postadresse" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Skriv inn et telefonnummer inkludert landskode (f.eks. +47 for Norge)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefon" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Du må skrive det samme passordet hver gang." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Passord" #: account/forms.py:67 msgid "Remember Me" msgstr "Husk meg" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Brukernavn" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Logg inn" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Brukernavn, e-post eller telefon" #: account/forms.py:117 msgid "Username or email" msgstr "Brukernavn eller e-post" #: account/forms.py:119 msgid "Username or phone" msgstr "Brukernavn eller telefon" #: account/forms.py:121 msgid "Email or phone" msgstr "E-post eller telefon" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Glemt ditt passord?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-post (igjen)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Bekreftelse av e-postadresse" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-post (valgfritt)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Brukernavn (valgfritt)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Du må skrive inn samme e-post hver gang." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Passord (igjen)" #: account/forms.py:591 msgid "Current Password" msgstr "Nåværende passord" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Nytt passord" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Nytt passord (igjen)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Kode" #: account/models.py:23 msgid "user" msgstr "bruker" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "e-postadresse" #: account/models.py:31 msgid "verified" msgstr "verifisert" #: account/models.py:32 msgid "primary" msgstr "primær" #: account/models.py:38 msgid "email addresses" msgstr "e-postadresser" #: account/models.py:142 msgid "created" msgstr "opprettet" #: account/models.py:143 msgid "sent" msgstr "sendt" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "nøkkel" #: account/models.py:149 msgid "email confirmation" msgstr "e-postbekreftelse" #: account/models.py:150 msgid "email confirmations" msgstr "e-postbekreftelser" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Se din bruker-ID" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Se din e-postadresse" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Se din grunnleggende profilinformasjon" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Gi tillatelser" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Jokertegn er ikke tillatt med mindre 'Tillat URI-jokertegn' er aktivert." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' inneholder mer enn ett jokertegn (*). Bare ett jokertegn per URI er " "tillatt." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Jokertegn er bare tillatt i vertsnavndelen av URI-en." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Autorisasjonskode" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Enhetskode" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Klientlegitimasjon" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Oppdateringstoken" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Konfidensiell" #: idp/oidc/models.py:44 msgid "Public" msgstr "Offentlig" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Omfanget(-ene) klienten har lov til å be om. Oppgi én verdi per linje, " "f.eks.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Hvis klienten ikke angir noe omfang, brukes disse standardomfangene. Oppgi " "én verdi per linje, f.eks.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "En liste over tillatte bevilgningstyper. Oppgi én verdi per linje, f.eks.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "En liste over tillatte opphav for kryssopprinnelsesforespørsler, én per " "linje." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Tillat jokertegn (*) i omdirigerings-URI-er og CORS-opphav. Når aktivert kan " "URI-er inneholde en enkelt stjerne for å matche underdomener." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "En liste over tillatte responstyper. Oppgi én verdi per linje, f.eks.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "klient" #: idp/oidc/models.py:116 msgid "clients" msgstr "klienter" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Du kan ikke legge til en e-postadresse til en konto beskyttet av " "tofaktorautentisering." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Du kan ikke deaktivere tofaktorautentisering." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Du kan ikke generere gjenopprettingskoder uten å ha tofaktorautentisering " "aktivert." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Du kan ikke aktivere tofaktorautentisering før du har verifisert e-" "postadressen din." #: mfa/adapter.py:141 msgid "Master key" msgstr "Hovednøkkel" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Reservenøkkel" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Nøkkel nr. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Gjenopprettingskoder" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP-autentiserer" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Autentiseringskode" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Passordløs" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Å aktivere passordløs drift lar deg logge inn med bare denne nøkkelen, men " "krever ytterligere sikkerhet som biometri eller PIN-beskyttelse." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "En konto med denne e-postadressen eksisterer allerede. Vennligst logg inn på " "den kontoen først, og koble deretter til din %s-konto." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Ugyldig token." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Kontoen din har ikke noe passord." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Din konto har ingen verifisert e-postadresse." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Du kan ikke koble fra din siste gjenværende tredjpartskonto." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Tredjepartskontoen er allerede tilknyttet en annen konto." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Sosiale kontoer" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "tilbyder" #: socialaccount/models.py:53 msgid "provider ID" msgstr "tilbyder-ID" #: socialaccount/models.py:57 msgid "name" msgstr "navn" #: socialaccount/models.py:59 msgid "client id" msgstr "klient-ID" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "App-ID eller konsumentnøkkel" #: socialaccount/models.py:64 msgid "secret key" msgstr "hemmelig nøkkel" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API-nøkkel, klient-nøkkel eller konsumentnøkkel" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Nøkkel" #: socialaccount/models.py:82 msgid "social application" msgstr "sosial applikasjon" #: socialaccount/models.py:83 msgid "social applications" msgstr "sosiale applikasjoner" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "siste innlogging" #: socialaccount/models.py:121 msgid "date joined" msgstr "ble med dato" #: socialaccount/models.py:122 msgid "extra data" msgstr "ekstra data" #: socialaccount/models.py:126 msgid "social account" msgstr "sosialkonto" #: socialaccount/models.py:127 msgid "social accounts" msgstr "sosialkontoer" #: socialaccount/models.py:161 msgid "token" msgstr "nøkkel" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) eller aksess token (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "hemmelig nøkkel" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) eller oppdateringsnøkkel (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "utgår den" #: socialaccount/models.py:175 msgid "social application token" msgstr "sosial applikasjonsnøkkel" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "sosial applikasjonsnøkler" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Ugyldig profildata" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Logg inn" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Avbryt" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "Ugyldig svar ved henting av token fra \"%s\". Svaret var: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Ugyldig respons ved henting av tilgangsnøkkel fra \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Ingen etterspørselsnøkler lagret for \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Ingen tilgangsnøkler lagret for \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Ingen tilgang til private ressurser på \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Ugyldig respons ved henting av forespørselsnøkkel fra \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Inaktiv konto" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Denne kontoen er inaktiv." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Vi har sendt en kode til %(recipient)s. Koden utløper snart, så vennligst " "skriv den inn snart." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Bekreft" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Be om ny kode" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Bekreft tilgang" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Vennligst autentiser deg på nytt for å beskytte kontoen din." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Alternative valg" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "E-postverifisering" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Skriv inn e-postverifiseringskode" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Bruk en annen e-postadresse" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Logg inn" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Skriv inn innloggingskode" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Tilbakestilling av passord" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Skriv inn kode for tilbakestilling av passord" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Telefonverifisering" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Skriv inn telefonverifiseringskode" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Bruk et annet telefonnummer" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "E-postadresser" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Følgende e-postadresser er assosiert med din konto:" #: templates/account/email.html:25 msgid "Verified" msgstr "Verifisert" #: templates/account/email.html:29 msgid "Unverified" msgstr "Ikke verifisert" #: templates/account/email.html:34 msgid "Primary" msgstr "Primær" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Sett som primær" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Send verifikasjon på nytt" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Fjern" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Legg til e-postadresse" #: templates/account/email.html:70 msgid "Add Email" msgstr "Legg til e-post" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Er du sikker på at du vil fjerne valgt e-postadresse?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Du mottar denne e-posten fordi du eller noen andre prøvde å registrere\n" "en konto med e-postadressen:\n" "\n" "%(email)s\n" "\n" "Imidlertid eksisterer det allerede en konto med den e-postadressen. Hvis du " "har\n" "glemt dette, vennligst bruk prosedyren for glemt passord for å gjenopprette\n" "kontoen din:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Konto eksisterer allerede" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Hei fra %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Takk for at du bruker %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Du mottar denne e-posten fordi følgende endring ble gjort på kontoen din:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Hvis du ikke gjenkjenner denne endringen, vennligst ta nødvendige " "sikkerhetstiltak umiddelbart. Endringen på kontoen din stammer fra:\n" "\n" "- IP-adresse: %(ip)s\n" "- Nettleser: %(user_agent)s\n" "- Dato: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "E-posten din er endret fra %(from_email)s til %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "E-post endret" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "E-posten din er bekreftet." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "E-postbekreftelse" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Du mottar denne eposten fordi %(user_display)s har oppgitt din e-postadresse " "for å opprette en konto på %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Din e-postverifiseringskode er oppgitt nedenfor. Vennligst skriv den inn i " "det åpne nettleservinduet." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "For å bekrefte at dette stemmer, gå til %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Vennligst bekreft din e-postadresse" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "E-postadressen %(deleted_email)s er fjernet fra kontoen din." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "E-post fjernet" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Din innloggingskode er oppgitt nedenfor. Vennligst skriv den inn i det åpne " "nettleservinduet." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Denne e-posten kan trygt ignoreres hvis du ikke igangsatte denne handlingen." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Innloggingskode" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Passordet ditt er endret." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Passord endret" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Din kode for tilbakestilling av passord er oppgitt nedenfor. Vennligst skriv " "den inn i det åpne nettleservinduet." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Kode for tilbakestilling av passord" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Du mottar denne e-posten fordi du eller noen andre har bedt om tilbakestille " "passordet for din brukerkonto.\n" "Meldingen kan trygt ignoreres om du ikke har bedt om å tilbakestille " "passord. Klikk på linken nedenfor for å tilbakestille ditt passord." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "I tilfelle du har glemt det: Brukernavnet ditt er %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "E-post for gjenopprettelse av passord" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Passordet ditt er tilbakestilt." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Passordet ditt er satt." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Passord satt" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Du mottar denne e-posten fordi du, eller noen andre, prøvde å få tilgang til " "en konto med e-post %(email)s. Vi har imidlertid ingen registrering av en " "slik konto i vår database." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Hvis det var deg, kan du registrere en konto ved å bruke lenken nedenfor." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Ukjent konto" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "E-postadresse" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Nåværende e-post" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Endres til" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "E-postadressen din venter fortsatt på verifisering." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Avbryt endring" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Endre til" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Endre e-post" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Bekreft e-postadresse" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Vennligst bekreft at %(email)s er en e-" "postadresse for bruker %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Kan ikke bekrefte %(email)s fordi den allerede er bekreftet for en annen " "konto." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Denne e-postbekreftelseslenken er utgått eller ugyldig. Vennligst be om en ny e-postbekreftelse." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Hvis du ikke har opprettet en konto enda, kan du %(link)sregistrere " "deg%(end_link)s først." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Logg inn med en passnøkkel" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Send meg en innloggingskode" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Logg ut" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Er du sikker at du ønsker å logge ut?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Du kan ikke fjerne den primær e-postadressen (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Bekreftelsese-post er sendt til %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Du har bekreftet %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Fjern e-postadressen %(email)s." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Logget inn som %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Du er logget ut." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "En innloggingskode er sendt til %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Passordet er endret." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Passordet er satt." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "En bekreftelseskode er sendt til %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Du har verifisert telefonnummer %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Primær e-postadresse er satt." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Endre Passord" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Glemt passord?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Glemt passordet ditt? Skriv inn e-postadressen din, så får du en epost som " "lar deg tilbakestille passordet." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Tilbakestill passord" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "Ta kontakt hvis du har problemer med å tilbakestille passordet ditt." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Vi har sendt deg en e-post. Hvis du ikke har mottatt den, vennligst sjekk " "søppelpostmappen din. Ellers kontakt oss hvis du ikke mottar den i løpet av " "noen få minutter." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Ugyldig token" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Passordgjenopprettelseslinken er ugyldig – muligens fordi den allerede er " "brukt. Vennligst etterspør en ny " "tilbakestilling av passord." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Ditt passord er endret." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Sett passord" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Endre telefon" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Nåværende telefon" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Telefonnummeret ditt venter fortsatt på verifisering." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Endre telefon" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Skriv inn passordet ditt:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Du vil motta en spesiell kode for passordløs innlogging." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Be om kode" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Andre innloggingsalternativer" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Registrer deg" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Registrer deg" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Har du allerede en konto? Vennligst %(link)slogg inn%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Registrer deg med en passnøkkel" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Registrer med passnøkkel" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Andre alternativer" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Registrering stengt" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Beklager, men registrering er for tiden stengt." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Notat" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Du er allerede logget inn som %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Advarsel:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Du har for tiden ingen e-postadresse satt opp. Du bør legge til en e-" "postadresse slik at du kan motta varsler, tilbakestille passord osv." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Bekreft din e-postadresse" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Vi har sendt deg en e-post for verifisering. Følg lenken for å fullføre " "registreringen. Hvis du ikke ser verifiserings-e-posten i innboksen, sjekk " "søppelpostmappen. Vennligst kontakt oss hvis du ikke mottar verifiserings-e-" "posten innen noen minutter." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Denne delen av nettstedet krever at vi verifiserer at\n" "du er den du sier du er. For dette formålet krever vi at du\n" "verifiserer eierskap til din e-postadresse. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Vi har sendt deg en e-post for\n" "bekreftelse. Vennligst klikk på lenken i e-posten. Hvis du ikke ser " "verifiserings-e-posten i innboksen din, sjekk søppelpostmappen.\n" "Ellers kontakt oss hvis du ikke mottar den innen noen få minutter." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Merk: du kan fortsatt endre din e-" "postadresse." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Meldinger:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Meny:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Kontotilkoblinger" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Tofaktorautentisering" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Økter" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Autoriser" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s ønsker tilgang til din %(site_name)s-konto." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Skriv inn enhetskode" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Skriv inn koden som vises på enheten din." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Fortsett" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Bekreft enhet" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Vennligst bekreft koden som vises på din %(client_name)s for å autorisere " "denne enheten." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Avslå" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Enhet autorisert" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Du har autorisert %(client_name)s-enheten din." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Enhet avslått" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Autorisering for %(client_name)s-enheten din er avslått." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Feil" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Forbli innlogget" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Kontoen din er beskyttet av tofaktorautentisering. Vennligst skriv inn en " "autentiseringskode:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Et nytt sett med gjenopprettingskoder for tofaktorautentisering er generert." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Nye gjenopprettingskoder generert" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Autentiseringsapp aktivert." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Autentiseringsapp aktivert" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Autentiseringsapp deaktivert." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Autentiseringsapp deaktivert" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "En ny sikkerhetsnøkkel er lagt til." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Sikkerhetsnøkkel lagt til" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "En sikkerhetsnøkkel er fjernet." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Sikkerhetsnøkkel fjernet" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Autentiseringsapp" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Autentisering med en autentiseringsapp er aktiv." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Ingen autentiseringsapp er aktiv." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Deaktiver" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Aktiver" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Sikkerhetsnøkler" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Du har lagt til %(count)s sikkerhetsnøkkel." msgstr[1] "Du har lagt til %(count)s sikkerhetsnøkler." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Ingen sikkerhetsnøkler er lagt til." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Administrer" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Legg til" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Gjenopprettingskoder" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Det er %(unused_count)s av %(total_count)s gjenopprettingskoder tilgjengelig." msgstr[1] "" "Det er %(unused_count)s av %(total_count)s gjenopprettingskoder tilgjengelig." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Ingen gjenopprettingskoder er satt opp." #: templates/mfa/index.html:96 msgid "View" msgstr "Vis" #: templates/mfa/index.html:102 msgid "Download" msgstr "Last ned" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Generer" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Et nytt sett med gjenopprettingskoder er generert." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Sikkerhetsnøkkel lagt til." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Sikkerhetsnøkkel fjernet." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Skriv inn en autentiseringskode:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "" "Du er i ferd med å generere et nytt sett med gjenopprettingskoder for " "kontoen din." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Denne handlingen vil ugyldiggjøre dine eksisterende koder." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Er du sikker?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Ubrukte koder" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Last ned koder" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Generer nye koder" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Aktiver autentiseringsapp" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "For å beskytte kontoen din med tofaktorautentisering, skann QR-koden " "nedenfor med autentiseringsappen din. Skriv deretter inn verifiseringskoden " "generert av appen." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Autentiseringshemmelighet" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Du kan lagre denne hemmeligheten og bruke den til å installere " "autentiseringsappen på nytt senere." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Deaktiver autentiseringsapp" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Du er i ferd med å deaktivere autentisering via autentiseringsapp. Er du " "sikker?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Stol på denne nettleseren?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Hvis du velger å stole på denne nettleseren, vil du ikke bli bedt om en " "verifiseringskode neste gang du logger inn." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Stol på i %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Ikke stol på" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Legg til sikkerhetsnøkkel" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Fjern sikkerhetsnøkkel" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Er du sikker på at du vil fjerne denne sikkerhetsnøkkelen?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Bruk" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Passnøkkel" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Sikkerhetsnøkkel" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Denne nøkkelen indikerer ikke om den er en passnøkkel." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Uspesifisert" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Lagt til %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Sist brukt %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Rediger" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Rediger sikkerhetsnøkkel" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Lagre" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Opprett passnøkkel" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Du er i ferd med å opprette en passnøkkel for kontoen din. Siden du kan " "legge til flere nøkler senere, kan du bruke et beskrivende navn for å skille " "dem." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Opprett" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Denne funksjonaliteten krever JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Tredjeparts innlogging mislyktes" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Det oppstod en feil ved forsøk på å logge inn via din tredjepartskonto." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Du kan logge inn på kontoen din ved å bruke en av følgende tredjeparts-" "kontoer:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Du har for tiden ingen tredjeparts-kontoer tilkoblet denne kontoen." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Legg til en tredjepartskonto" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "En tredjepartskonto fra %(provider)s er koblet til kontoen din." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Tredjepartskonto tilkoblet" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "En tredjepartskonto fra %(provider)s er frakoblet kontoen din." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Tredjepartskonto frakoblet" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Koble til %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Du er i ferd med å koble til en ny tredjepartskonto fra %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Logg inn via %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "Du er i ferd med å logge inn med en tredjepartskonto fra %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Innlogging kansellert" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Du bestemt deg for å kansellere innlogging til siden ved å bruke en av dine " "eksisterende kontoer. Om dette var ved en feil, vennligst fortsett til innloggingen." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Tredjepartskontoen er tilkoblet." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Tredjepartskontoen er frakoblet." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Du er på vei til å bruke din %(provider_name)s konto for å logge inn på\n" "%(site_name)s. Som et siste steg, vennligst fullfør følgende skjema:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Eller bruk en tredjepart" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Logget ut av alle andre økter." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Startet" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP-adresse" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Nettleser" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Sist sett" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Nåværende" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Logg ut andre økter" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Brukerøkter" #: usersessions/models.py:94 msgid "session key" msgstr "øktnøkkel" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Kontotilkoblinger" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Passordet må være minst {0} tegn." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Hei fra %(site_name)s!\n" #~ "\n" #~ "Du mottar denne e-postadressen fordi du eller noen andre har etterspurt " #~ "et passord for din brukerkonto.\n" #~ "Meldingen kan trygt ignoreres om du ikke foretok denne etterspørselen. " #~ "Klikk på linken nedenfor for å gjennopprette ditt passord." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "Følgende e-postadresser er assosiert med din konto:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Bekreft e-postadresse" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Vennligst logg in med en\n" #~ "av dine eksisterende tredjeparts kontoer på %(site_name)s. Eller, registrer deg\n" #~ "og logg inn nedenfor:" #~ msgid "or" #~ msgstr "eller" #~ msgid "change password" #~ msgstr "Endre passord" #~ msgid "OpenID Sign In" #~ msgstr "OpenID-innlogging" #~ msgid "This email address is already associated with another account." #~ msgstr "Denne e-postadressen er tilknyttet en annen konto." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Vi har sendt deg en e-post. Vennligst kontakt oss om du ikke mottar den " #~ "innen et par minutter." ================================================ FILE: allauth/locale/nl/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Free Software Foundation, Inc. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-07-25 20:26+0200\n" "Last-Translator: Jos van Leeuwen \n" "Language-Team: Dutch \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.11-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Dit account is momenteel inactief." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Je kunt je primaire e-mailadres niet verwijderen." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Dit e-mailadres is al aan dit account gekoppeld." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Het door jou opgegeven e-mailadres en/of wachtwoord is niet correct." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "" "Het door jou opgegeven telefoonnummer en/of wachtwoord is niet correct." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Er is al een gebruiker geregistreerd met dit e-mailadres." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Geef je huidige wachtwoord op." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Ongeldige code." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Ongeldig wachtwoord." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Ongeldige of verlopen sleutel." #: account/adapter.py:79 msgid "Invalid login." msgstr "Ongeldige login." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "De wachtwoordherstel-sleutel is niet geldig." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Je kunt niet meer dan %d e-mailadressen toevoegen." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Er is al een gebruiker geregistreerd met dit telefoonnummer." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Te veel inlogpogingen. Probeer het later nogmaals." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Dit e-mailadres is niet bij ons bekend." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Het telefoonnummer is niet aan een gebruikersaccount toegewezen." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Je primaire e-mailadres moet geverifieerd zijn." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Deze gebruikersnaam mag je niet gebruiken, kies een andere." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Je gebruikersnaam en/of wachtwoord zijn incorrect." #: account/adapter.py:98 msgid "Please select only one." msgstr "Selecteer er slechts één." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "De nieuwe waarde moet verschillen van de huidige waarde." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Wees geduldig, je gaat te snel." #: account/adapter.py:826 msgid "Use your password" msgstr "Gebruik je wachtwoord" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Gebruik authenticator-app of code" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Gebruik een beveiligingssleutel" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} gemarkeerd als geverifieerd." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Kon {email} niet als geverifieerd markeren." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Markeer de geselecteerde email addressen als geverifieerd" #: account/apps.py:11 msgid "Accounts" msgstr "Accounts" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-mail" #: account/fields.py:19 msgid "Email address" msgstr "E-mailadres" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Voer een telefoonnummer in, inclusief landcode (bijv. +31 voor NL)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefoon" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Je moet hetzelfde wachtwoord twee keer intoetsen." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Wachtwoord" #: account/forms.py:67 msgid "Remember Me" msgstr "Onthouden" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Gebruikersnaam" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Login" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Gebruikersnaam, e-mail, of telefoonnummer" #: account/forms.py:117 msgid "Username or email" msgstr "Gebruikersnaam of e-mail" #: account/forms.py:119 msgid "Username or phone" msgstr "Gebruikersnaam of telefoonnummer" #: account/forms.py:121 msgid "Email or phone" msgstr "E-mail of telefoonnummer" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Wachtwoord vergeten?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-mail (bevestigen)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Bevestig e-mailadres" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-mail (optioneel)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Gebruikersnaam (optioneel)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Je moet hetzelfde e-mailadres twee keer intoetsen." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Wachtwoord (bevestigen)" #: account/forms.py:591 msgid "Current Password" msgstr "Huidig wachtwoord" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Nieuw wachtwoord" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Nieuw wachtwoord (bevestigen)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Code" #: account/models.py:23 msgid "user" msgstr "gebruiker" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "e-mailadres" #: account/models.py:31 msgid "verified" msgstr "geverifieerd" #: account/models.py:32 msgid "primary" msgstr "Primair" #: account/models.py:38 msgid "email addresses" msgstr "e-mailadressen" #: account/models.py:142 msgid "created" msgstr "aangemaakt" #: account/models.py:143 msgid "sent" msgstr "verstuurd" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "sleutel" #: account/models.py:149 msgid "email confirmation" msgstr "e-mailadres bevestiging" #: account/models.py:150 msgid "email confirmations" msgstr "e-mailadres bevestigingen" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Bekijk je gebruikers-ID" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Bekijk je e-mailadres" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Bekijk je basisprofielinformatie" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Machtigingen verlenen" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Jokertekens zijn niet toegestaan tenzij 'URI-jokertekens toestaan' is " "ingeschakeld." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' bevat meer dan één jokerteken (*). Er is slechts één jokerteken per " "URI toegestaan." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Jokertekens zijn alleen toegestaan in het hostnaamgedeelte van de URI." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Autorisatiecode" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Apparaatcode" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Clientreferenties" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Vernieuwingstoken" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Vertrouwelijk" #: idp/oidc/models.py:44 msgid "Public" msgstr "Openbaar" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "De scope(s) die de client mag opvragen. Geef één waarde per regel, bijv.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Als de client geen scope opgeeft, worden deze standaard scopes gebruikt. " "Geef één waarde per regel, bijv.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Een lijst van toegestane grant types. Geef één waarde per regel, bijv.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Een lijst van toegestane origins voor cross-origin verzoeken, één per regel." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Jokertekens (*) toestaan in omleidings-URI's en CORS-origins. Wanneer " "ingeschakeld kunnen URI's een enkele asterisk bevatten om subdomeinen te " "matchen." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Een lijst van toegestane response types. Geef één waarde per regel, bijv.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "client" #: idp/oidc/models.py:116 msgid "clients" msgstr "clients" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Je kunt geen e-mailadres toevoegen aan een account dat beveiligd is met twee-" "factor-authenticatie is." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Je kunt twee-factor-authenticatie niet deactiveren." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Je kunt geen herstelcodes genereren zonder dat twee-factor-authenticatie " "actief is." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Je moet eerst je e-mailadres verifiëren voordat je twee-factor-authenticatie " "kunt activeren." #: mfa/adapter.py:141 msgid "Master key" msgstr "Hoofdsleutel" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Reservesleutel" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Sleutel nr. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Herstelcodes" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP Authenticator" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Authenticator code" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Wachtwoordloos" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Door wachtwoordloos inloggen mogelijk te maken kun je je aanmelden met deze " "sleutel. Dit brengt extra vereisten met zich mee, zoals biometrische " "verificatie of PIN-bescherming." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Er bestaat al een account met dit e-mailadres. Meld je eerst aan met dit " "account, verbind daarna je %s account." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Ongeldige sleutel." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Je account heeft geen wachtwoord ingesteld." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Je account heeft geen geverifieerd e-mailadres." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Je kunt het laatste externe account niet verwijderen." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Dit externe account is al gekoppeld aan een ander account." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Sociale accounts" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "provider" #: socialaccount/models.py:53 msgid "provider ID" msgstr "provider ID" #: socialaccount/models.py:57 msgid "name" msgstr "naam" #: socialaccount/models.py:59 msgid "client id" msgstr "client id" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "App ID, of consumer key" #: socialaccount/models.py:64 msgid "secret key" msgstr "geheime sleutel" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API secret, client secret, of consumer secret" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Sleutel" #: socialaccount/models.py:82 msgid "social application" msgstr "social application" #: socialaccount/models.py:83 msgid "social applications" msgstr "social applications" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "laatst ingelogd" #: socialaccount/models.py:121 msgid "date joined" msgstr "datum toegetreden" #: socialaccount/models.py:122 msgid "extra data" msgstr "extra data" #: socialaccount/models.py:126 msgid "social account" msgstr "social account" #: socialaccount/models.py:127 msgid "social accounts" msgstr "social accounts" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) of toegangs-token (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "token geheim" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) of ververs token (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "verloopt op" #: socialaccount/models.py:175 msgid "social application token" msgstr "social applicatie token" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "social applicatie tokens" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Ongeldige profiel data" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Login" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Annuleren" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Ongeldig antwoord ontvangen tijdens het ophalen van een verzoeksleutel van " "\"%s\", antwoord: \"%s\"." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "" "Ongeldig antwoord ontvangen tijdens het ophalen van een toegangssleutel van " "\"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Geen verzoeksleutel opgeslagen voor \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Geen toegangssleutel opgeslagen voor \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Geen toegang tot privé data bij \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "" "Ongeldig antwoord ontvangen tijdens het ophalen van een verzoeksleutel van " "\"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Account inactief" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Dit account is niet actief" #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "We hebben een code naar %(recipient)s gestuurd. De code verloopt binnenkort, " "dus voer hem snel in." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Bevestigen" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Nieuwe code aanvragen" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Toegang bevestigen" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Authenticeer opnieuw om de beveiliging van je account te waarborgen." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Andere opties" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "E-mailadres verificatie" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Voer de e-emailadres verificatie code in:" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Gebruik een ander e-mailadres" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Aanmelden" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Aanmeldcode invullen" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Nieuw wachtwoord" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Voer wachtwoordherstelcode in" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Telefoonverificatie" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Telefoonverificatiecode invoeren" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Gebruik een ander telefoonnummer" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "E-mailadressen" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "De volgende e-mailadressen zijn gekoppeld aan jouw account:" #: templates/account/email.html:25 msgid "Verified" msgstr "Geverifieerd" #: templates/account/email.html:29 msgid "Unverified" msgstr "Ongeverifieerd" #: templates/account/email.html:34 msgid "Primary" msgstr "Primair" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Maak primair" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Stuur verificatie e-mail opnieuw" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Verwijder" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Voeg e-mailadres toe" #: templates/account/email.html:70 msgid "Add Email" msgstr "E-mail toevoegen" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Wil je het geselecteerde e-mailadres echt verwijderen?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Je ontvangt deze e-mail omdat jij of iemand anders geprobeerd heeft een " "account\n" "te registreren met dit e-mail adres:\n" "\n" "%(email)s\n" "\n" "Echter, er bestaat al een account met dit e-mail adres. Als je dit " "vergeten\n" "was, gebruik dan de wachtwoord vergeten procedure om de toegang tot je " "account\n" "te herstellen:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Account bestaat al" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Hallo van %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Bedankt voor het gebruiken van %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "Je ontvangt deze e-mail vanwege de volgende wijziging aan je account:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Herken je deze wijziging niet? Neem dan onmiddelijk voorzorgsmaatregelen en " "beveilig je account. Deze wijzigingen komen van:\n" "\n" "- IP adres: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Datum: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Je email adres is gewijzigd van %(from_email)s naar %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "E-mailadres gewijzigd" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Je e-mailadres is bevestigd." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "E-mailadres bevestiging" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Je ontvangt deze e-mail omdat gebruiker %(user_display)s van\n" "%(site_domain)s dit als e-mailadres heeft opgegeven om te koppelen\n" "aan zijn of haar account." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Hieronder staat je e-mailadres verificatiecode. Vul deze in in het browser " "venster dat nog open staat." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Om te bevestigen dat dit correct is, bezoek: %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Bevestig je e-mailadres" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "Email adres %(deleted_email)s is ontkoppeld van je account." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "E-mailadres verwijderd" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Hieronder staat je aanmeldcode. Vul deze in in het browser venster dat nog " "open staat." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "Je kunt deze mail gerust negeren als je dit niet zelf gedaan hebt." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Aanmeldcode" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Je wachtwoord is gewijzigd." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Wachtwoord gewijzigd" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Je wachtwoordherstelcode staat hieronder. Voer deze code in je geopende " "browservenster in." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Wachtwoordherstelcode" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Je ontvangt deze mail omdat er een verzoek is ingelegd om je wachtwoord " "opnieuw\n" "in te stellen. Je kunt deze mail gerust negeren als je dit niet zelf gedaan\n" "hebt. Anders, klik op de volgende link om je wachtwoord opnieuw in te " "stellen." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Deze link hoort bij je account met gebruikersnaam %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Nieuw wachtwoord" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Je wachtwoord is opnieuw ingesteld." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Je wachtwoord is ingesteld." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Wachtwoord ingesteld" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Je ontvang deze e-mail omdat jij, of iemand anders, een poging gedaan heeft " "een account met e-mailadres %(email)s te gebruiken. Echter, dit account " "bestaat niet." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Als jij dit wel hebt gedaan, dan kun je je via de volgende link registreren." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Onbekend account" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "E-mailadres" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Huidig email" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Wordt veranderd in" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Je e-mailadres wacht op verificatie." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Annuleer wijziging" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Wijzig naar" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "E-mail wijzigen" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Bevestig e-mailadres" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Bevestig dat %(email)s een e-mailadres is " "voor gebruiker %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Het e-mail adres %(email)s kon niet worden geverifieerd omdat het gekoppeld " "is aan een ander account." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Deze e-mail verificatie link is verlopen of niet geldig. Dien een\n" "nieuw e-mail verificatie verzoek in." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Als je nog geen account hebt %(link)sregistreer%(end_link)s je dan eerst." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Meld je aan met een passkey" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Stuur een aanmeldcode" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Afmelden" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Weet je zeker dat je wilt afmelden?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "You kunt je primaire e-mailadres (%(email)s) niet verwijderen." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Bevestigings e-mail verzonden aan %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Je hebt het e-mailadres %(email)s bevestigd" #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "E-mailadres %(email)s verwijderd." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Je bent nu ingelogd als %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Je bent afgemeld." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Er is een aanmeldcode verzonden naar %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Wachtwoord wijziging geslaagd." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Je wachtwoord is gewijzigd." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Er is een verificatiecode verzonden naar %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Je hebt telefoonnummer %(phone)s geverifieerd." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Primair e-mailadres ingesteld." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Wachtwoord wijzigen" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Wachtwoord vergeten?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Wachtwoord vergeten? Vul je e-mailadres in en we sturen je een e-mail " "waarmee je een nieuw wachtwoord kunt instellen." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Herstel mijn wachtwoord" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Neem a.u.b. contact met ons op als het niet lukt je wachtwoord opnieuw in te " "stellen." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Volg de link in de verificatie e-mail om de controle af te ronden. Neem " "a.u.b. contact met ons op als je de e-mail niet binnen enkele minuten " "ontvangt." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Ongeldige sleutel" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "De link om je wachtwoord opnieuw in te stellen is niet geldig. Mogelijk is " "deze al een keer gebruikt. Herstel je " "wachtwoord opnieuw." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Je wachtwoord is gewijzigd." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Zet wachtwoord" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Telefoon wijzigen" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Huidig telefoonnummer" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Je telefoonnummer wacht op verificatie." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Telefoon wijzigen" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Voer je wachtwoord in:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Je ontvangt een speciale code om je zonder wachtwoord aan te melden." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Code aanvragen" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Andere aanmeld-opties" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Registreren" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Registreren" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Heb je al een account? %(link)sMeld je dan aan%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Registreren met een passkey" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Passkey registratie" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Andere opties" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Registratie gesloten" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Helaas, maar je kunt je momenteel niet registreren." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Notitie" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Je bent al ingelogd als %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Waarschuwing:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Het is raadzaam een e-mailadres toe te voegen zodat je notificaties kunt " "ontvangen, je wachtwoord opnieuw kunt instellen, enz." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Verifieer je e-mailadres" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "We hebben een e-mail verstuurd ter verificatie. Volg de link in deze mail om " "je registratie af te ronden. Neem a.u.b. contact met ons op als je deze e-" "mail niet binnen enkele minuten ontvangt." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Voor dit gedeelte van de site is het nodig dat we je identiteit\n" "verifiëren. Via een verificatie e-mail kunnen we controleren dat je\n" "daadwerkelijk toegang hebt tot het opgegeven e-mailadres." #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Volg de link in de verificatie e-mail om de controle af te ronden. Neem " "a.u.b. contact met ons op als je de e-mail niet binnen enkele minuten " "ontvangt." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Merk op: je kunt altijd je e-mail " "adres wijzigen." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Berichten:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menu:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Account Connecties" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Twee-factor-authenticatie" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sessies" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Autoriseren" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s wil toegang tot je %(site_name)s account." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Voer apparaatcode in" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Voer de code in die op je apparaat wordt weergegeven." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Ga verder" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Apparaat bevestigen" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Bevestig de code die op je %(client_name)s wordt weergegeven om dit apparaat " "te autoriseren." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Weigeren" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Apparaat geautoriseerd" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Je hebt je %(client_name)s apparaat succesvol geautoriseerd." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Apparaat geweigerd" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Autorisatie voor je %(client_name)s apparaat is geweigerd." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Fout" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Ingelogd blijven" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Je account is beveiligd met twee-factor-authenticatie. Voer alstublieft een " "authenticatiecode in:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "Er zijn nieuwe herstelcodes gegenereerd." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Nieuwe herstelcodes gegenereerd" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Authenticator-app geactiveerd." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Authenticator-app geactiveerd" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Authenticator-app gedeactiveerd." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Authenticator-app gedeactiveerd" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Een nieuwe beveiligingssleutel is toegevoegd." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Beveiligingssleutel toegevoegd" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Een beveiligingssleutel is verwijderd." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Beveiligingssleutel verwijderd" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Authenticator-app" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Authenticatie middels een authenticator-app is actief." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Een authenticator-app is niet geactiveerd." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Deactiveer" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Activeer" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Beveiligingssleutels" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Je hebt %(count)s beveiligingssleutel toegevoegd." msgstr[1] "Je hebt %(count)s beveiligingssleutels toegevoegd." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Er zijn geen beveiligingssleutels gekoppeld." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Beheren" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Voeg toe" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Herstelcodes" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Er is nog %(unused_count)s van de %(total_count)s herstelcodes beschikbaar." msgstr[1] "" "Er zijn nog %(unused_count)s van de %(total_count)s herstelcodes beschikbaar." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Er zijn geen herstelcodes ingesteld." #: templates/mfa/index.html:96 msgid "View" msgstr "Bekijk" #: templates/mfa/index.html:102 msgid "Download" msgstr "Downloaden" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Genereer" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Er zijn nieuwe herstelcodes gegenereerd." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Beveiligingssleutel toegevoegd." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Beveiligingssleutel verwijderd." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Voer een authenticator code in:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Je staat op het punt nieuwe herstelcodes te genereren voor je account." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Hierdoor zullen eerdere herstelcodes niet langer werken." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Weet je dit zeker?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Ongebruikte codes" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Codes downloaden" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Nieuwe codes genereren" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Activeer authenticator-app" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Beveilig je account met twee-factor-authenticatie. Scan eerst de " "onderstaande QR code met je authenticator app, en vul vervolgens de " "resulterende verificatie code in." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Authenticator geheim" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Je kunt deze code bewaren en later gebruiken om je authenticator app opnieuw " "te installeren." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Deactiveer authenticator-app" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Je staat op het punt authenticatie middels een authenticator-app te " "deactiveren. Wil je dit echt?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Vertrouw deze browser?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Als je ervoor kiest om deze browser te vertrouwen, wordt je de volgende keer " "dat je inlogt niet om een verificatiecode gevraagd." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Vertrouw voor %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Vertrouw niet" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Voeg beveiligingssleutel toe" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Verwijder beveiligingssleutel" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Weet je zeker dat je deze beveiligingssleutel wilt verwijderen?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Gebruik" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Passkey" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Beveiligingssleutel" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Het is onduidelijk of deze sleutel een passkey betreft." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Ongespecificeerd" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Toegevoegd op %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Laatst gebruikt %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Bewerken" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Bewerk beveiligingssleutel" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Opslaan" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Passkey aanmaken" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Je staat op het punt een passkey aan te maken. Aangezen je later extra " "sleutels kunt toevoegen kun je deze een herkenbare naam geven om de sleutels " "uit elkaar te kunnen houden." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Maak aan" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Deze functionaliteit vereist JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Aanmelden Mislukt" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Er is een fout opgetreden toen we je wilde inloggen via je externe account." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "Je kunt jezelf aanmelden met een van de volgende externe accounts:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Je hebt momenteel geen externe accounts gekoppeld." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Voeg een extern account toe" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Een extern account van %(provider)s is gekoppeld aan je account." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Extern account gekoppeld" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "Een extern account van %(provider)s is ontkoppeld van je account." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Extern account ontkoppeld" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Koppel %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "" "Je staat op het punt om een nieuw account van %(provider)s aan je account te " "koppelen." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Aanmelden via %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "Je staat op het punt om jezelf aan te melden via %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Aanmelden geannuleerd" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Je hebt het aanmelden via een extern account geannuleerd. Als dit een " "vergissing was, meld je dan opnieuw aan." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Het externe account is gekoppeld." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Het externe account is ontkoppeld." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Om bij %(site_name)s in te kunnen loggen via %(provider_name)s hebben we de " "volgende gegevens nodig:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Of gebruik een derde partij" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Afgemeld van alle andere sessies." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Gestart op" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP adres" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Browser" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Laatst gezien op" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Huidig" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Elders uitloggen" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Gebruikerssessies" #: usersessions/models.py:94 msgid "session key" msgstr "sessie sleutel" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Account Connecties" #~ msgid "Use security key or device" #~ msgstr "Gebruik een beveiligingssleutel" #~ msgid "Add Security Key or Device" #~ msgstr "Beveiligingssleutel toevoegen" #~ msgid "Add key or device" #~ msgstr "Beveiligingssleutel toevoegen" #~ msgid "Security Keys and Devices" #~ msgstr "Beveiligingssleutels" #~ msgid "You have not added any security keys/devices." #~ msgstr "Je hebt geen beveiligingssleutels toegevoegd." #~ msgid "Edit Security Key or Device" #~ msgstr "Bewerk beveiligingssleutel" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Het wachtwoord moet minimaal {0} tekens bevatten." #, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Je ontvang deze e-mail omdat er een verzoek is ingelegd om het wachtwoord " #~ "te\n" #~ "veranderen van account met e-mailadres %(email)s. Echter, dit account " #~ "bestaat\n" #~ "niet.\n" #~ "\n" #~ "Je kunt deze e-mail negeren als jij dit verzoek niet zelf hebt ingelegd.\n" #~ "\n" #~ "Als jij dit wel hebt gedaan, dan kun je je via de volgende link " #~ "registreren." #~ msgid "The following email address is associated with your account:" #~ msgstr "De volgende e-mailadressen zijn gekoppeld aan jouw account:" #~ msgid "Change Email Address" #~ msgstr "Wijzig e-mailadres" #~ msgid "" #~ "To safeguard the security of your account, please enter your password:" #~ msgstr "" #~ "Om de beveiliging van je account te waarborgen, voert eerst je wachtwoord " #~ "in:" #, fuzzy #~ msgid "Regenerate" #~ msgstr "Genereer" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Meld je aan met een van je bestaande externe accounts. Of, registreer voor een %(site_name)s account en " #~ "meld je hiermee aan:" #~ msgid "or" #~ msgstr "of" #~ msgid "change password" #~ msgstr "Wachtwoord wijzigen" #~ msgid "OpenID Sign In" #~ msgstr "Aanmelden via OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "Dit e-mailadres is al geassocieerd met een ander account." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "We hebben je een e-mail verstuurd. Neem a.u.b. contact met ons op als je " #~ "deze niet binnen enkele minuten ontvangen hebt." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "Je login en wachtwoord komen niet overeen." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "" #~ "Gebruikersnamen mogen alleen letters, cijfers en @/./+/-/_ bevatten." #~ msgid "This username is already taken. Please choose another." #~ msgstr "Deze gebruikersnaam is al in gebruik. Kies a.u.b. een andere naam." #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "Je hebt bevestigd dat %(email)s een e-" #~ "mail adres is voor gebruiker %(user_display)s." #~ msgid "Thanks for using our site!" #~ msgstr "Bedankt voor het gebruik van onze site!" #~ msgid "Confirmation email sent to %(email)s" #~ msgstr "Bevestigings e-mail verzonden aan %(email)s" #~ msgid "Delete Password" #~ msgstr "Verwijder wachtwoord" #~ msgid "" #~ "You may delete your password since you are currently logged in using " #~ "OpenID." #~ msgstr "" #~ "Je kunt je wachtwoord verwijderen omdat je via OpenID bent ingelogd." #~ msgid "delete my password" #~ msgstr "Verwijder mijn wachtwoord" #~ msgid "Password Deleted" #~ msgstr "Wachtwoord verwijderd" ================================================ FILE: allauth/locale/pl/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR Free Software Foundation, Inc. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-08-20 01:01+0000\n" "Last-Translator: Wolf \n" "Language-Team: Polish \n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && " "(n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && " "n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" "X-Generator: Weblate 5.13\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Konto jest obecnie nieaktywne." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Nie możesz usunąć podstawowego adresu e-mail." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Ten adres e-mail jest już powiązany z tym kontem." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Podany adres e-mail i/lub hasło są niepoprawne." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Podane numer telefonu i/lub hasło są niepoprawne." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "W systemie jest już zarejestrowany użytkownik o tym adresie e-mail." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Proszę wpisz swoje obecne hasło." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Błędny kod." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Niewłaściwe hasło." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Nieprawidłowy lub wygasły klucz." #: account/adapter.py:79 msgid "Invalid login." msgstr "Niewłaściwy login." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Token resetowania hasła był nieprawidłowy." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Nie możesz dodać więcej niż %d adresów e-mail." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "" "W systemie jest już zarejestrowany użytkownik o tym adresie numerze telefonu." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Zbyt wiele nieudanych prób logowania. Spróbuj ponownie później." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Adres e-mail nie jest powiązany z żadnym kontem użytkownika." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Numer telefonu nie jest powiązany z żadnym kontem użytkownika." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Twój podstawowy adres e-mail musi być zweryfikowany." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Nie możesz użyć tej nazwy użytkownika. Proszę wybierz inną." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Podana nazwa użytkownika i/lub hasło są niepoprawne." #: account/adapter.py:98 msgid "Please select only one." msgstr "Wybierz tylko jeden." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Nowa wartość musi być inna niż bieżąca." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Bądź cierpliwy, wysyłasz zbyt wiele próśb." #: account/adapter.py:826 msgid "Use your password" msgstr "Użyj swojego hasła" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Użyj aplikacji Authenticator lub kodu" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Użyj klucza bezpieczeństwa" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "Oznaczono adres {email} jako zweryfikowany." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Nie udało się oznaczyć adresu {email} jako zweryfikowanego." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Oznacz wybrane adresy e-mail jako zweryfikowane" #: account/apps.py:11 msgid "Accounts" msgstr "Konta" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-mail" #: account/fields.py:19 msgid "Email address" msgstr "Adres e-mail" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Wprowadź numer telefonu wraz z kodem kraju (np. +1 dla USA)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefon" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Musisz wpisać za każdym razem to samo hasło." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Hasło" #: account/forms.py:67 msgid "Remember Me" msgstr "Pamiętaj mnie" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Nazwa użytkownika" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Login" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Nazwa użytkownika, e-mail lub numer telefonu" #: account/forms.py:117 msgid "Username or email" msgstr "Nazwa użytkownika lub e-mail" #: account/forms.py:119 msgid "Username or phone" msgstr "Nazwa użytkownika lub numer telefonu" #: account/forms.py:121 msgid "Email or phone" msgstr "E-mail lub numer telefonu" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Zapomniałeś hasła?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-mail (ponownie)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Potwierdzenie adresu e-mail" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-mail (opcjonalnie)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Nazwa użytkownika (opcjonalnie)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Musisz wpisać za każdym razem ten sam e-mail." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Hasło (ponownie)" #: account/forms.py:591 msgid "Current Password" msgstr "Obecne hasło" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Nowe hasło" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Nowe hasło (ponownie)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Kod" #: account/models.py:23 msgid "user" msgstr "użytkownik" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "adres e-mail" #: account/models.py:31 msgid "verified" msgstr "zweryfikowany" #: account/models.py:32 msgid "primary" msgstr "podstawowy" #: account/models.py:38 msgid "email addresses" msgstr "adresy e-mail" #: account/models.py:142 msgid "created" msgstr "utworzono" #: account/models.py:143 msgid "sent" msgstr "wysłano" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "klucz" #: account/models.py:149 msgid "email confirmation" msgstr "potwierdzenie adresu e-mail" #: account/models.py:150 msgid "email confirmations" msgstr "potwierdzenia adresów e-mail" #: headless/apps.py:7 msgid "Headless" msgstr "Bezgłowy" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Wyświetl swój identyfikator użytkownika" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Wyświetl swoje adresy e-mail" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Wyświetl podstawowe informacje o swoim profilu" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Udziel uprawnień" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Znaki wieloznaczne nie są dozwolone, chyba że opcja 'Zezwalaj na znaki " "wieloznaczne w URI' jest włączona." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' zawiera więcej niż jeden znak wieloznaczny (*). Dozwolony jest " "tylko jeden znak wieloznaczny na URI." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "" "Znaki wieloznaczne są dozwolone tylko w części URI zawierającej nazwę hosta." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Kod autoryzacyjny" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Kod urządzenia" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Dane uwierzytelniające klienta" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Odśwież token" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Poufny" #: idp/oidc/models.py:44 msgid "Public" msgstr "Publiczny" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Zakres(y), o które klient może poprosić. Podaj jedną wartość w każdym " "wierszu, np.: openid(ENTER)profil(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Jeśli klient nie określi żadnego zakresu, używane są te domyślne zakresy. " "Podaj jedną wartość w każdym wierszu, np.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Lista dozwolonych typów uprawnień. Podaj jedną wartość w każdym wierszu, " "np.: authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "Lista dozwolonych źródeł dla żądań międzydomenowych, jedno na wiersz." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Zezwalaj na znaki wieloznaczne (*) w URI przekierowań i źródłach CORS. Po " "włączeniu URI mogą zawierać pojedynczą gwiazdkę w celu dopasowania subdomen." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Lista dozwolonych typów odpowiedzi. Podaj jedną wartość w każdym wierszu, " "np.: kod(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "klient" #: idp/oidc/models.py:116 msgid "clients" msgstr "klienci" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Nie można dodać adresu e-mail do konta chronionego dwuskładnikową " "autoryzacją." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Nie można dezaktywować uwierzytelniania dwuskładnikowego." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Nie można wygenerować kodów odzyskiwania bez włączonego uwierzytelniania " "dwuskładnikowego." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Nie możesz aktywować uwierzytelniania dwuskładnikowego dopóki nie " "zweryfikujesz swojego adresu e-mail." #: mfa/adapter.py:141 msgid "Master key" msgstr "Klucz główny" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Klucz zapasowy" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Numer klucza {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Kody odzyskiwania" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "Autentykator TOTP" #: mfa/models.py:26 msgid "WebAuthn" msgstr "Web Authentication" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Kod uwierzytelniający" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Bez hasła" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Włączenie logowania bez hasła pozwala na logowanie się przy użyciu tylko " "tego klucza, wiąże się jednak z koniecznością spełnienia dodatkowych " "wymagań, takich jak ochrona biometryczna lub PIN." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Istnieje już konto dla tego adresu email. Zaloguj się najpierw na to konto, " "a następnie połącz swoje konto %s." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Nieprawidłowy token." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Twoje konto nie posiada hasła." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Twoje konto nie ma zweryfikowanego adresu e-mail." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Nie możesz odłączyć ostatniego dostępnego konta innej firmy." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "To konto innej firmy jest już połączone z innym kontem." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Konta społecznościowe" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "dostawca usług" #: socialaccount/models.py:53 msgid "provider ID" msgstr "identyfikator dostawcy" #: socialaccount/models.py:57 msgid "name" msgstr "nazwa" #: socialaccount/models.py:59 msgid "client id" msgstr "id klienta" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "ID aplikacji lub klucz odbiorcy" #: socialaccount/models.py:64 msgid "secret key" msgstr "klucz prywatny" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "Klucz prywatny API, klienta lub odbiorcy" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Klucz" #: socialaccount/models.py:82 msgid "social application" msgstr "aplikacja społecznościowa" #: socialaccount/models.py:83 msgid "social applications" msgstr "aplikacje społecznościowe" #: socialaccount/models.py:118 msgid "uid" msgstr "Unique Identification Number" #: socialaccount/models.py:120 msgid "last login" msgstr "Ostatnie logowanie" #: socialaccount/models.py:121 msgid "date joined" msgstr "data dołączenia" #: socialaccount/models.py:122 msgid "extra data" msgstr "dodatkowe dane" #: socialaccount/models.py:126 msgid "social account" msgstr "konto społecznościowe" #: socialaccount/models.py:127 msgid "social accounts" msgstr "konta społecznościowe" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) lub token dostępu (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "sekretny token" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) lub odświerz token (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "wygasa" #: socialaccount/models.py:175 msgid "social application token" msgstr "token aplikacji społecznościowej" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "tokeny aplikacji społecznościowych" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Nieprawidłowe dane profilu" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Login" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Anuluj" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Nieprawidłowa odpowiedź podczas uzyskiwania tokenu żądania od „%s”. " "Odpowiedź brzmiała: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Błędna odpowiedź podczas pobierania tokena autoryzacji z \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Brak tokena zapisanego dla \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Brak zapisanego tokena autoryzacji \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Brak dostępu do prywatnych zasobów na \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Błędna odpowiedź podczas pobierania tokena z \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Konto nieaktywne" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "To konto jest nieaktywne." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Wysłaliśmy kod do %(recipient)s. Kod wkrótce wygaśnie, więc prosimy o jego " "szybkie wprowadzenie." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Potwierdź" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Poproś o nowy kod" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Potwierdź dostęp" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Aby zabezpieczyć swoje konto, dokonaj ponownego uwierzytelnienia." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Opcje alternatywne" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Weryfikacja adresu e-mail" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Wprowadź kod weryfikacyjny e-mail" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Użyj innego adresu e-mail" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Zaloguj" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Wprowadź kod logowania" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Resetowanie hasła" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Wprowadż kod resetowania hasła" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Weryfikacja telefoniczna" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Wprowadź kod weryfikacyjny telefonu" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Użyj innego numeru telefonu" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Adresy e-mail" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Poniższe adresy e-mail są powiązane z Twoim kontem:" #: templates/account/email.html:25 msgid "Verified" msgstr "Zweryfikowany" #: templates/account/email.html:29 msgid "Unverified" msgstr "Brak weryfikacji" #: templates/account/email.html:34 msgid "Primary" msgstr "Podstawowy" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Uczyń podstawowym" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Prześlij ponownie wiadomość weryfikacyjną" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Usuń" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Dodaj adres e-mail" #: templates/account/email.html:70 msgid "Add Email" msgstr "Dodaj e-mail" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Czy naprawdę chcesz usunąć wybrany adres e-mail?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Otrzymujesz tę wiadomość e-mail, ponieważ Ty lub ktoś inny próbował założyć\n" "konto przy użyciu adresu e-mail:\n" "\n" "%(email)s\n" "\n" "Konto o tym adresie e-mail już istnieje. Jeśli\n" "o tym zapomniałeś, skorzystaj z procedury odzyskiwania hasła, aby odzyskać\n" "swoje konto:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Konto już istnieje" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Witamy z %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Dziękujemy za korzystanie z %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Otrzymujesz tę wiadomość e-mail, ponieważ na Twoim koncie wprowadzono " "następującą zmianę:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Jeśli nie rozpoznajesz tej zmiany, natychmiast podejmij odpowiednie środki " "bezpieczeństwa. Zmiana na Twoim koncie pochodzi z:\n" "\n" "- Adres IP: %(ip)s\n" "- Przeglądarka: %(user_agent)s\n" "- Data: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Twój adres e-mail został zmieniony z %(from_email)s na %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Zmieniono adres e-mail" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Twój adres e-mail został potwierdzony." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Potwierdzenie e-mailem" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Otrzymujesz tę wiadomość e-mail, ponieważ użytkownik %(user_display)s podał " "Twój adres e-mail w celu zarejestrowania konta w domenie %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Poniżej znajduje się kod weryfikacyjny Twojego adresu e-mail. Wprowadź go w " "otwartym oknie przeglądarki." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Aby potwierdzić poprawność, przejdź do %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Proszę potwierdź adresy e-mail" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "Adresy e-mail %(deleted_email)s został usunięty z Twojego konta." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "e-mail usunięty" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Poniżej znajduje się Twój kod logowania. Wprowadź go w otwartym oknie " "przeglądarki." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Jeśli nie podjąłeś tej czynności, możesz bezpiecznie zignorować tę wiadomość." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Kod logowania" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Zmieniono Twoje hasło." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Hasło zmienione" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Poniżej znajduje się kod resetowania hasła. Wprowadź go w otwartym oknie " "przeglądarki." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Kod resetowania hasła" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Otrzymujesz tę wiadomość e-mail, ponieważ Ty lub ktoś inny poprosił o " "zresetowanie hasła do Twojego konta użytkownika.\n" "Możesz ją bezpiecznie zignorować, jeśli nie prosiłeś o zresetowanie hasła. " "Kliknij poniższy link, aby zresetować hasło." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "" "Na wszelki wypadek przypominamy, że Twoja nazwa użytkownika to %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "E-mail z resetowaniem hasła" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Twoje hasło zostało zresetowane." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Twoje hasło zostało ustawione ." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Hasło ustawione" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Otrzymujesz tę wiadomość e-mail, ponieważ Ty lub ktoś inny próbował uzyskać " "dostęp do konta o adresie e-mail %(email)s. Jednak w naszej bazie danych nie " "ma żadnego wpisu o takim koncie." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "Jeśli to Ty, możesz założyć konto korzystając z poniższego linku." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Nieznane konto" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Adresy email" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Obecny adres e-mail" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Zmiana na" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Twój adres e-mail nadal oczekuje na weryfikację." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Anuluj zmianę" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Zmień na" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Zmień e-mail" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Potwierdź adres e-mail" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Potwierdź, że %(email)s jest adresem e-mail " "użytkownika %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Nie można potwierdzić adresu %(email)s, ponieważ został on już potwierdzony " "z innego konta." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Ten link potwierdzający e-mail wygasł lub jest nieprawidłowy. Proszę wysłać nową prośbę o potwierdzenie e-mailem." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Jeżeli nie masz jeszcze konta, to proszę %(link)szarejestruj się%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Zaloguj się za pomocą klucza dostępu" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Wyślij mi kod logowania" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Wyloguj się" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Czy na pewno chcesz się wylogować ?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Nie możesz usunąć podstawowego adresu e-mail (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "E-mail z potwierdzeniem został wysłany na adres %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Adres %(email)s został potwierdzony." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Adres e-mail %(email)s został usunięty." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Zalogowano jako %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Wylogowano." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Kod logowania został wysłany do %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Hasło zostało zmienione." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Hasło zostało ustawione." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Kod weryfikacyjny został wysłany na numer %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Zweryfikowałeś numer telefonu %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Podstawowy adres e-mail został ustawiony." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Zmień hasło" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Nie pamiętasz hasła?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Zapomniałeś hasła? Wpisz poniżej swój adres e-mail, a wyślemy Ci wiadomość e-" "mail umożliwiającą jego zresetowanie." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Zresetuj moje hasło" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Skontaktuj się z nami, jeśli masz problem ze zresetowaniem swojego hasła." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Wysłaliśmy Ci wiadomość e-mail. Jeśli jej nie otrzymałeś, sprawdź folder ze " "spamem. Jeśli nie otrzymasz jej w ciągu kilku minut, skontaktuj się z nami." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Nieprawidłowy klucz" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Link do resetowania hasła był nieprawidłowy, prawdopodobnie dlatego, że " "został już użyty. Poproś o nowe " "zresetowanie hasła." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Twoje hasło zostało zmienione." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Ustaw hasło" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Zmień telefon" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Obecny telefon" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Twój numer telefonu nadal oczekuje na weryfikację." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Zmień telefon" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Wprowadż hasło:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Otrzymasz specjalny kod umożliwiający logowanie bez podawania hasła." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Kod żądania" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Inne opcje logowania" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Zarejestruj się" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Zarejestruj się" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Masz już konto? Jeżeli tak, to %(link)szaloguj się%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Zarejestruj się za pomocą klucza dostępu" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Rejestracja za pomocą klucza dostępu" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Inne opcje" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Rejestracja zamknięta" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Przepraszamy, ale w tej chwili rejestracja jest zamknięta." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Uwaga" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Jesteś już zalogowany/-a jako %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Ostrzeżenie:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Obecnie nie masz skonfigurowanego adresu e-mail. Powinieneś go dodać, aby " "otrzymywać powiadomienia, resetować hasło itp." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Zweryfikuj swój adres e-mail" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Wysłaliśmy do Ciebie wiadomość e-mail z prośbą o weryfikację. Kliknij podany " "link, aby dokończyć proces rejestracji. Jeśli nie widzisz wiadomości " "weryfikacyjnej w swojej głównej skrzynce odbiorczej, sprawdź folder ze " "spamem. Skontaktuj się z nami, jeśli nie otrzymasz wiadomości weryfikacyjnej " "w ciągu kilku minut." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Ta część witryny wymaga od nas weryfikacji,\n" "że jesteś tym, za kogo się podajesz. W tym celu wymagamy od Ciebie\n" "potwierdzenia własności Twojego adresu e-mail. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Wysłaliśmy do Ciebie wiadomość e-mail w celu\n" "weryfikacji. Kliknij link w tej wiadomości. Jeśli nie widzisz wiadomości " "weryfikacyjnej w głównej skrzynce odbiorczej, sprawdź folder ze spamem. W " "przeciwnym razie,\n" "jeśli nie otrzymasz jej w ciągu kilku minut, skontaktuj się z nami." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Uwaga: nadal możesz zmienić swój " "adres e-mail." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Wiadomości:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menu:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Połączone konta" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Autoryzacja dwuskładnikowa" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sesje" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "autoryzuj" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s chce uzyskać dostęp do Twojego konta %(site_name)s." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Wprowadź kod urządzenia" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Wprowadź kod wyświetlony na Twoim urządzeniu." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Kontynuuj" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Potwierdź urządzenie" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Potwierdź kod wyświetlony na %(client_name)s, aby autoryzować to urządzenie." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Odmów" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Urządzenie autoryzowane" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Pomyślnie autoryzowałeś urządzenie %(client_name)s ." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Urządzenie odrzucone" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Autoryzacja Twojego urządzenia %(client_name)s została odrzucona." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Błąd" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Pozostań zalogowany" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Twoje konto jest chronione przez uwierzytelnianie dwuskładnikowe. Proszę " "wprowadzić kod uwierzytelniający:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Wygenerowano nowy zestaw kodów odzyskiwania uwierzytelniania " "dwuskładnikowego." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Wygenerowano nowe kody odzyskiwania" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Aplikacja autoryzacyjna została aktywowana." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Aplikacja uwierzytelniająca aktywowana" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Aplikacja autoryzacyjna została dezaktywowana." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Aplikacja uwierzytelniająca została dezaktywowana" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Dodano nowy klucz bezpieczeństwa." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Dodano klucz bezpieczeństwa" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Twoje hasło zostało usunięte." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Usunięto klucz bezpieczeństwa" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Aplikacja autoryzacyjna" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Autoryzacja za pomocą aplikacji autoryzacyjnej jest aktywna." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Aplikacja autoryzacyjna nie jest aktywna." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Dezaktywuj" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Aktywuj" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Klucze bezpieczeństwa" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Dodałeś klucz bezpieczeństwa %(count)s." msgstr[1] "Dodałeś klucze bezpieczeństwa %(count)s." msgstr[2] "Dodałeś klucze bezpieczeństwa %(count)s." msgstr[3] "Dodałeś klucze bezpieczeństwa %(count)s." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Nie dodano żadnych kluczy bezpieczeństwa." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Zarządzaj" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Dodaj" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Kody odzyskiwania" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Dostępny jest %(unused_count)s z %(total_count)s kodów odzyskiwania." msgstr[1] "" "Dostępnych jest %(unused_count)s z %(total_count)s kodów odzyskiwania." msgstr[2] "" "Dostępnych jest %(unused_count)s z %(total_count)s kodów odzyskiwania." msgstr[3] "" "Dostępnych jest %(unused_count)s z %(total_count)s kodów odzyskiwania." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Nie ustawiono żadnych kodów odzyskiwania." #: templates/mfa/index.html:96 msgid "View" msgstr "Widok" #: templates/mfa/index.html:102 msgid "Download" msgstr "Pobierz" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Generuj" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Wygenerowano nowy zestaw kodów odzyskiwania." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Dodano klucz bezpieczeństwa." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Usunięto klucz bezpieczeństwa." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Wprowadź kod uwierzytelniający:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Zaraz wygenerujesz nowy zestaw kodów odzyskiwania dla Twojego konta." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Ta czynność spowoduje unieważnienie istniejących kodów." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Jesteś pewny?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Niewykorzystane kody" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Pobierz kody" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Wygeneruj nowe kody" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Aktywuj aplikację uwierzytelniającą" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Aby zabezpieczyć swoje konto za pomocą uwierzytelniania dwuskładnikowego, " "zeskanuj poniższy kod QR za pomocą aplikacji uwierzytelniającej. Następnie " "wprowadź poniżej kod weryfikacyjny wygenerowany przez aplikację." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Tajny klucz uwierzytelniający" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Możesz przechować ten sekret i użyć go później do ponownej instalacji " "aplikacji uwierzytelniającej." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Wyłącz aplikację uwierzytelniającą" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Zamierzasz wyłączyć uwierzytelnianie oparte na aplikacji Authenticator. " "Jesteś pewien?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Czy ufasz tej przeglądarce?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Jeśli zdecydujesz się zaufać tej przeglądarce, przy następnym logowaniu nie " "zostaniesz poproszony o podanie kodu weryfikacyjnego." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Zaufaj przez %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Nie ufaj" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Dodaj klucz bezpieczeństwa" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Usuń klucz bezpieczeństwa" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Czy na pewno chcesz usunąć ten klucz bezpieczeństwa?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Stosowanie" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Klucz uniwersalny" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Klucz bezpieczeństwa" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Klucz ten nie wskazuje, czy jest kluczem dostępu." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Nieokreślony" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Dodano w %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Ostatnio użyte %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Edytuj" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Edytuj klucz bezpieczeństwa" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Zapisz" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Utwórz klucz dostępu" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Zamierzasz utworzyć klucz dostępu do swojego konta. Ponieważ później możesz " "dodać kolejne klucze, możesz użyć opisowej nazwy, aby je rozróżnić." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Utwórz" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Do korzystania z tej funkcjonalności wymagany jest JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Błąd logowania za pośrednictwem strony trzeciej" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Wystąpił błąd podczas próby logowania za pośrednictwem konta innej firmy." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Możesz zalogować się na swoje konto przy użyciu dowolnego z następujących " "kont osób trzecich:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Obecnie nie masz żadnych kont innych firm połączonych z tym kontem." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Dodaj konto innej firmy" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Do Twojego konta zostało podłączone konto innej firmy od %(provider)s." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Połączono konto innej firmy" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "Konto innej firmy od %(provider)s zostało odłączone od Twojego konta." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Konto firmy trzeciej odłączone" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Połącz z %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Zamierzasz podłączyć nowe konto innej firmy od %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Zaloguj się za pomocą %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" "Zamierzasz zalogować się przy użyciu konta zewnętrznego dostawcy " "%(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Logowanie anulowane" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Wybrano anulowanie logowania przez jedno z istniejących kont. Jeżeli to była " "pomyłka, proszę przejdź do zaloguj." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Konto firmy trzeciej zostało połączone." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Konto osoby trzeciej zostało odłączone." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Masz zamiar użyć konta %(provider_name)s do zalogowania się w \n" "%(site_name)s. Jako ostatni krok, proszę wypełnij formularz:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Lub skorzystaj z serwisów społecznościowych" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Wylogowano ze wszystkich pozostałych sesji." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Rozpoczęto o" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "Adres IP" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Przeglądarka" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Ostatnio widziany w" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Obecne" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Wyloguj się z innych sesji" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Sesje użytkowników" #: usersessions/models.py:94 msgid "session key" msgstr "Klucz sesji" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Połączone konta" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Hasło musi składać się z co najmniej {0} znaków." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Otrzymujesz tę wiadomość, ponieważ Ty lub ktoś inny poprosił o " #~ "zresetowanie hasła do Twojego konta.\n" #~ "Niniejszą wiadomość możesz spokojnie zignorować, jeżeli prośba nie " #~ "pochodziła od Ciebie. Kliknij w link poniżej, aby zresetować hasło." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "Poniższe adresy e-mail są powiązane z Twoim kontem:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Potwierdź adres e-mail" #~ msgid "" #~ "To safeguard the security of your account, please enter your password:" #~ msgstr "Dla bezpieczeństwa konta, prosimy o podanie hasła:" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Proszę zaloguj się jednym\n" #~ "z Twoich zewnętrznych kont. Lub %(link)szarejestruj się \n" #~ "w %(site_name)s i zaloguj poniżej:" #~ msgid "or" #~ msgstr "lub" #~ msgid "change password" #~ msgstr "zmień hasło" #~ msgid "OpenID Sign In" #~ msgstr "Zaloguj przez OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "Ten adres e-mail jest już powiązany z innym kontem." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Wysłaliśmy Ci e-mail. Proszę skontaktuj się z nami, jeśli go nie " #~ "otrzymasz w ciągu paru minut." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "Login i/lub hasło, które podałeś, są niepoprawne." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "" #~ "Nazwa użytkownika może zawierać tylko litery, cyfry oraz znaki @/./+/-/_." #~ msgid "This username is already taken. Please choose another." #~ msgstr "Nazwa użytkownika jest już w użyciu. Proszę wybierz inną." #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "Potwierdziłeś, że adres %(email)s jest " #~ "adresem e-mail użytkownika %(user_display)s." #~ msgid "Thanks for using our site!" #~ msgstr "Dziękujemy za używanie naszej strony!" #~ msgid "Socialaccount" #~ msgstr "Konta społecznościowe" #~ msgid "Key (Stack Exchange only)" #~ msgstr "Klucz (tylko dla Stack Exchange)" #~ msgid "Confirmation email sent to %(email)s" #~ msgstr "Wiadomość z potwierdzeniem została wysłana na adres %(email)s" #~ msgid "Delete Password" #~ msgstr "Skasuj hasło" #~ msgid "" #~ "You may delete your password since you are currently logged in using " #~ "OpenID." #~ msgstr "Możesz skasować swoje hasło jeśli używasz OpenID." #~ msgid "delete my password" #~ msgstr "skasuj moje hasło" #~ msgid "Password Deleted" #~ msgstr "Hasło Skasowane" ================================================ FILE: allauth/locale/pt_BR/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Renato De Giovanni , 2022 # cacarrara , 2014 # Fábio C. Barrionuevo da Luz , 2013-2014 # Rodrigo , 2013 # Rodrigo , 2013-2014 msgid "" msgstr "" "Project-Id-Version: django-allauth\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2026-02-12 01:09+0000\n" "Last-Translator: Lisandro Guerra Simões Pires \n" "Language-Team: Portuguese (Brazil) \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.16-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Esta conta está atualmente inativa." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Você não pode remover seu endereço de e-mail principal." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Este endereço de e-mail já está associado a esta conta." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "O endereço de e-mail e/ou senha especificados estão incorretos." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "O número de telefone e/ou senha especificados estão incorretos." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Um usuário já está registrado com este endereço de e-mail." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Por favor, digite sua senha atual." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Código incorreto." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Senha incorreta." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Chave inválida ou expirada." #: account/adapter.py:79 msgid "Invalid login." msgstr "Conexão inválida." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "O token de redefinição de senha era inválido." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Você não pode adicionar mais de %d endereços de e-mail." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Um usuário já está registrado com este número de telefone." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Muitas tentativas de login falharam. Tente novamente mais tarde." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "O endereço de e-mail não está atribuído a nenhuma conta de usuário." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "O número de telefone não está atribuído a nenhuma conta de usuário" #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Seu endereço de e-mail principal deve ser verificado." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "" "Nome de usuário não pode ser utilizado. Por favor, use outro nome de usuário." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "O nome de usuário e/ou senha especificados estão incorretos." #: account/adapter.py:98 msgid "Please select only one." msgstr "Escolha apenas um, por favor." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "O novo valor deve ser diferente do atual." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Seja paciente, você está enviando muitas requisições." #: account/adapter.py:826 msgid "Use your password" msgstr "Use sua senha" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Use um aplicativo autenticador ou código" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Use uma chave de segurança" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "Marcado {email} como verificado." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Falha ao marcar {email} como verificado." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Marcar os endereços de e-mail selecionados como verificados" #: account/apps.py:11 msgid "Accounts" msgstr "Contas" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-mail" #: account/fields.py:19 msgid "Email address" msgstr "Endereço de e-mail" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" "Entre com um número de telefone incluindo o código de país (ex. +55 para " "Brasil)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefone" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Você deve digitar a mesma senha todas as vezes." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Senha" #: account/forms.py:67 msgid "Remember Me" msgstr "Lembrar de Mim" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Nome de usuário" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Login" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Nome de usuário, e-mail ou telefone" #: account/forms.py:117 msgid "Username or email" msgstr "Nome de usuário ou e-mail" #: account/forms.py:119 msgid "Username or phone" msgstr "Nome de usuário ou telefone" #: account/forms.py:121 msgid "Email or phone" msgstr "E-mail ou telefone" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Esqueceu sua senha?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-mail (novamente)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Confirmação de endereço de e-mail" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-mail (opcional)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Nome de usuário (opcional)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Você deve digitar o mesmo e-mail todas as vezes." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Senha (novamente)" #: account/forms.py:591 msgid "Current Password" msgstr "Senha atual" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Nova senha" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Nova senha (novamente)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Código" #: account/models.py:23 msgid "user" msgstr "usuário" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "endereço de e-mail" #: account/models.py:31 msgid "verified" msgstr "verificado" #: account/models.py:32 msgid "primary" msgstr "principal" #: account/models.py:38 msgid "email addresses" msgstr "endereços de e-mail" #: account/models.py:142 msgid "created" msgstr "criado" #: account/models.py:143 msgid "sent" msgstr "enviado" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "chave" #: account/models.py:149 msgid "email confirmation" msgstr "confirmação de e-mail" #: account/models.py:150 msgid "email confirmations" msgstr "confirmações de e-mail" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Ver seu ID de usuário" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Ver seu endereço de e-mail" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Ver suas informações básicas de perfil" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Conceder permissões" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Caracteres curinga não são permitidos a menos que 'Permitir curingas em " "URIs' esteja habilitado." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "A URI '{}' contém mais de um curinga (*). Apenas um curinga por URI é " "permitido." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Curingas são permitidos apenas na parte do nome de host da URI." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Código de autorização" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Código de dispositivo" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Credenciais do cliente" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Token de atualização" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Confidencial" #: idp/oidc/models.py:44 msgid "Public" msgstr "Público" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "O(s) escopo(s) que o cliente pode solicitar. Forneça um valor por linha, " "ex.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Caso o cliente não especifique nenhum escopo, estes escopos padrão são " "utilizados. Forneça um valor por linha, ex.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Uma lista de tipos de concessão permitidos. Forneça um valor por linha, ex.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Uma lista de origens permitidas para requisições cross-origin, uma por linha." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Permitir curingas (*) em URIs de redirecionamento e origens CORS. Quando " "habilitado, URIs podem conter um único asterisco para corresponder a " "subdomínios." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Uma lista de tipos de resposta permitidos. Forneça um valor por linha, ex.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "cliente" #: idp/oidc/models.py:116 msgid "clients" msgstr "clientes" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Você não pode adicionar um endereço de e-mail a uma conta protegida por " "autenticação de dois fatores." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Você não pode desativar a autenticação de dois fatores." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Você não pode gerar códigos de recuperação sem ter a autenticação de dois " "fatores ativada." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Você não pode ativar a autenticação de dois fatores até que tenha verificado " "seu endereço de e-mail." #: mfa/adapter.py:141 msgid "Master key" msgstr "Chave mestre" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Chave de backup" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Chave nº {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Códigos de recuperação" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "Autenticador TOTP" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Código do autenticador" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Sem senha" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Ativar a operação sem senha permite que você faça login apenas com esta " "chave, mas impõe requisitos adicionais, como biometria ou proteção com PIN." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Uma conta já existe com este endereço de e-mail. Faça login nessa conta " "primeiro e depois conecte sua conta %s." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Token inválido." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Sua conta não tem senha configurada." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Sua conta não tem um endereço de e-mail verificado." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Você não pode desconectar sua última conta de terceiros conectada." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "A conta de terceiros já está conectada a uma conta diferente." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Contas sociais" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "provedor" #: socialaccount/models.py:53 msgid "provider ID" msgstr "ID do provedor" #: socialaccount/models.py:57 msgid "name" msgstr "nome" #: socialaccount/models.py:59 msgid "client id" msgstr "ID do cliente" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "ID do aplicativo ou chave do consumidor" #: socialaccount/models.py:64 msgid "secret key" msgstr "chave secreta" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "Segredo da API, segredo do cliente ou segredo do consumidor" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Chave" #: socialaccount/models.py:82 msgid "social application" msgstr "aplicativo social" #: socialaccount/models.py:83 msgid "social applications" msgstr "aplicativos sociais" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "último login" #: socialaccount/models.py:121 msgid "date joined" msgstr "data de entrada" #: socialaccount/models.py:122 msgid "extra data" msgstr "dados adicionais" #: socialaccount/models.py:126 msgid "social account" msgstr "conta social" #: socialaccount/models.py:127 msgid "social accounts" msgstr "contas sociais" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) ou token de acesso (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "segredo do token" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) ou token de atualização (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "expira em" #: socialaccount/models.py:175 msgid "social application token" msgstr "token de aplicativo social" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "tokens de aplicativos sociais" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Dados de perfil inválidos" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Login" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Cancelar" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Resposta inválida ao obter o token de solicitação de \"%s\". A resposta foi: " "%s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Resposta inválida ao obter o token de acesso de \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Nenhum token de solicitação salvo para \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Nenhum token de acesso salvo para \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Sem acesso a recursos privados em \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Resposta inválida ao obter o token de solicitação de \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Conta inativa" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Esta conta está inativa." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Enviamos um código para %(recipient)s. O código expira em breve, então " "insira-o logo." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Confirmar" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Solicitar novo código" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Confirmar acesso" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Por favor, reautentique para proteger sua conta." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Opções alternativas" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Verificação de e-mail" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Insira o código de verificação de e-mail" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Usar um endereço de e-mail diferente" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Entrar" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Insira o código de entrada" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Redefinição de senha" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Insira o código de redefinição de senha" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Verificação de telefone" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Insira o código de verificação de telefone" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Usar um número de telefone diferente" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Endereços de e-mail" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Os seguintes endereços de e-mail estão associados à sua conta:" #: templates/account/email.html:25 msgid "Verified" msgstr "Verificado" #: templates/account/email.html:29 msgid "Unverified" msgstr "Não verificado" #: templates/account/email.html:34 msgid "Primary" msgstr "Principal" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Tornar principal" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Reenviar verificação" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Remover" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Adicionar endereço de e-mail" #: templates/account/email.html:70 msgid "Add Email" msgstr "Adicionar e-mail" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Você realmente deseja remover o endereço de e-mail selecionado?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Você está recebendo este e-mail porque você ou outra pessoa tentou se " "inscrever para\n" "uma conta usando o endereço de e-mail:\n" "\n" "%(email)s\n" "\n" "No entanto, uma conta com este endereço de e-mail já existe. Caso você " "tenha\n" "esquecido disso, por favor use o procedimento de recuperação de senha para\n" "recuperar sua conta:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Conta já existente" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Olá da %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Obrigado por usar %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Você está recebendo este e-mail porque a seguinte alteração foi feita em sua " "conta:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Se você não reconhece essa alteração, por favor tome as devidas precauções " "de segurança imediatamente. A alteração em sua conta se origina de:\n" "\n" "- Endereço IP: %(ip)s\n" "- Navegador: %(user_agent)s\n" "- Data: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Seu e-mail foi alterado de %(from_email)s para %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "E-mail alterado" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Seu e-mail foi confirmado." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Confirmação de e-mail" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Você está recebendo este e-mail porque o usuário %(user_display)s forneceu " "seu\n" "endereço de e-mail para registrar uma conta em %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Seu código de verificação de e-mail está listado abaixo. Por favor, insira-o " "na janela do seu navegador aberta." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Para confirmar que isso está correto, acesse %(activate_url)s." #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Por favor, confirme seu endereço de e-mail." #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "O endereço de e-mail %(deleted_email)s foi removido de sua conta." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "E-mail removido" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Seu código de entrada está listado abaixo. Por favor, insira-o na janela do " "seu navegador aberta." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Este e-mail pode ser ignorado com segurança se você não iniciou esta ação." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Código de entrada" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Sua senha foi alterada." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Senha alterada" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Seu código de redefinição de senha está listado abaixo. Por favor, insira-o " "na janela do seu navegador aberta." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Código de redefinição de senha" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Você está recebendo este e-mail porque você ou outra pessoa solicitou a " "redefinição de senha para sua conta de usuário.\n" "Este e-mail pode ser ignorado com segurança se você não solicitou a " "redefinição de senha. Clique no link abaixo para redefinir sua senha." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Caso tenha esquecido, seu nome de usuário é %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "E-mail de redefinição de senha" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Sua senha foi redefinida." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Sua senha foi definida." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Senha definida" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Você está recebendo este e-mail porque você, ou outra pessoa, tentou acessar " "uma conta com o e-mail %(email)s. No entanto, não temos nenhum registro de " "tal conta em nosso banco de dados." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "Se foi você, pode se inscrever para uma conta usando o link abaixo." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Conta desconhecida" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Endereço de e-mail" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "E-mail atual" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Alterando para" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Seu endereço de e-mail ainda está aguardando verificação." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Cancelar alteração" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Alterar para" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Alterar e-mail" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Confirmar endereço de e-mail" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Por favor, confirme que %(email)s é um " "endereço de e-mail para o usuário %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Incapaz de confirmar %(email)s porque já foi confirmado por uma conta " "diferente." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Este link de confirmação de e-mail expirou ou é inválido. Por favor, emita uma nova solicitação de confirmação de e-mail." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Se você ainda não criou uma conta, por favor, %(link)scadastre-" "se%(end_link)s primeiro." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Entrar com uma chave de segurança" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Envie-me um código de entrada" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Sair" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Tem certeza de que deseja sair?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Você não pode remover seu endereço de e-mail principal (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "E-mail de confirmação enviado para %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Você confirmou %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Endereço de e-mail removido %(email)s." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Conectado com sucesso como %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Você saiu." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Um código de entrada foi enviado para %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Senha alterada com sucesso." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Senha definida com sucesso." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Um código de verificação foi enviado para %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Você verificou o número de telefone %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Endereço de e-mail principal definido." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Alterar senha" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Esqueceu a senha?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Esqueceu sua senha? Insira seu endereço de e-mail abaixo e nós enviaremos um " "e-mail permitindo que você a redefina." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Redefinir minha senha" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Por favor, entre em contato conosco se tiver algum problema ao redefinir sua " "senha." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Enviamos um e-mail para você. Se você não o recebeu, por favor, verifique " "sua pasta de spam. Caso contrário, entre em contato conosco se não o receber " "em alguns minutos." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Token inválido" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "O link de redefinição de senha era inválido, possivelmente porque já foi " "utilizado. Por favor, solicite uma nova " "redefinição de senha." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Sua senha foi alterada." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Definir senha" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Alterar telefone" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Telefone atual" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Seu número de telefone ainda está aguardando verificação." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Alterar telefone" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Insira sua senha:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Você receberá um código especial para uma entrada sem senha." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Solicitar código" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Outras opções de entrada" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Cadastrar-se" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Cadastrar-se" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Já tem uma conta? Então, por favor, %(link)ssign in%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Cadastrar-se usando uma chave de segurança" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Cadastro com chave de segurança" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Outras opções" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Cadastro fechado" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Desculpe, mas o cadastro está atualmente fechado." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Nota" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Você já está conectado como %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Aviso:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Atualmente, você não tem nenhum endereço de e-mail configurado. Você deve " "realmente adicionar um endereço de e-mail para que possa receber " "notificações, redefinir sua senha, etc." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Verifique seu endereço de e-mail" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Enviamos um e-mail para você para verificação. Siga o link fornecido para " "finalizar o processo de cadastro. Se você não encontrar o e-mail de " "verificação em sua caixa de entrada principal, verifique sua pasta de spam. " "Entre em contato conosco se não receber o e-mail de verificação dentro de " "alguns minutos." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Esta parte do site exige que verifiquemos que você é quem afirma ser. Para " "esse fim, exigimos que você\n" "verifique a propriedade do seu endereço de e-mail. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Enviamos um e-mail para você para verificação. Por favor, clique no link " "dentro desse e-mail. Se você não encontrar o e-mail de verificação em sua " "caixa de entrada principal, verifique sua pasta de spam. Caso contrário, " "entre em contato conosco se não o receber dentro de alguns minutos." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Nota: você ainda pode alterar seu " "endereço de e-mail." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Mensagens:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menu:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Conexões de conta" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Autenticação de dois fatores" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sessões" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Autorizar" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s deseja acessar sua conta %(site_name)s." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Insira o código do dispositivo" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Insira o código exibido no seu dispositivo." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Continuar" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Confirmar dispositivo" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Por favor, confirme o código exibido no seu %(client_name)s para autorizar " "este dispositivo." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Negar" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Dispositivo autorizado" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Você autorizou com sucesso seu dispositivo %(client_name)s." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Dispositivo negado" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "A autorização para o seu dispositivo %(client_name)s foi negada." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Erro" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Manter conectado" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Sua conta está protegida por autenticação de dois fatores. Por favor, insira " "um código do autenticador:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Um novo conjunto de códigos de recuperação da autenticação de dois fatores " "foi gerado." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Novos códigos de recuperação gerados" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Aplicativo autenticador ativado." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Aplicativo autenticador ativado" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Aplicativo autenticador desativado." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Aplicativo autenticador desativado" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Uma nova chave de segurança foi adicionada." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Chave de segurança adicionada" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Uma chave de segurança foi removida." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Chave de segurança removida" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Aplicativo autenticador" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "A autenticação usando um aplicativo autenticador está ativa." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Um aplicativo autenticador não está ativo." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Desativar" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Ativar" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Chaves de segurança" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Você adicionou %(count)s chave de segurança." msgstr[1] "Você adicionou %(count)s chaves de segurança." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Nenhuma chave de segurança foi adicionada." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Gerenciar" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Adicionar" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Códigos de recuperação" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Há %(unused_count)s de %(total_count)s códigos de recuperação disponíveis." msgstr[1] "" "Há %(unused_count)s de %(total_count)s códigos de recuperação disponíveis." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Nenhum código de recuperação configurado." #: templates/mfa/index.html:96 msgid "View" msgstr "Visualizar" #: templates/mfa/index.html:102 msgid "Download" msgstr "Baixar" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Gerar" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Um novo conjunto de códigos de recuperação foi gerado." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Chave de segurança adicionada." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Chave de segurança removida." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Insira um código do autenticador:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "" "Você está prestes a gerar um novo conjunto de códigos de recuperação para " "sua conta." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Esta ação invalidará seus códigos existentes." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Você tem certeza?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Códigos não utilizados" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Baixar códigos" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Gerar novos códigos" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Ativar aplicativo autenticador" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Para proteger sua conta com autenticação de dois fatores, escaneie o código " "QR abaixo com seu aplicativo autenticador. Em seguida, insira o código de " "verificação gerado pelo aplicativo abaixo." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Segredo do autenticador" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Você pode armazenar este segredo e usá-lo para reinstalar seu aplicativo " "autenticador posteriormente." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Desativar aplicativo autenticador" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Você está prestes a desativar a autenticação baseada em aplicativo " "autenticador. Você tem certeza?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Confiar neste navegador?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Se você optar por confiar neste navegador, não será solicitado um código de " "verificação na próxima vez que entrar." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Confiar por %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Não confiar" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Adicionar chave de segurança" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Remover chave de segurança" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Tem certeza de que deseja remover esta chave de segurança?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Uso" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Chave de segurança" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Chave de segurança" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Esta chave não indica se é uma chave de segurança." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Não especificado" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Adicionado em %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Último uso em %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Editar" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Editar chave de segurança" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Salvar" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Criar chave de segurança" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Você está prestes a criar uma chave de segurança para sua conta. Como você " "pode adicionar chaves adicionais posteriormente, pode usar um nome " "descritivo para diferenciar as chaves." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Criar" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Esta funcionalidade requer JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Falha no login de terceiros" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "Ocorreu um erro ao tentar fazer login via sua conta de terceiros." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Você pode fazer login em sua conta usando qualquer uma das seguintes contas " "de terceiros:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Atualmente, você não tem contas de terceiros conectadas a esta conta." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Adicionar uma conta de terceiros" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Uma conta de terceiros de %(provider)s foi conectada à sua conta." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Conta de terceiros conectada" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "Uma conta de terceiros de %(provider)s foi desconectada de sua conta." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Conta de terceiros desconectada" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Conectar %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "" "Você está prestes a conectar uma nova conta de terceiros de %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Entrar via %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" "Você está prestes a fazer login usando uma conta de terceiros de " "%(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Login cancelado" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Você decidiu cancelar o login em nosso site usando uma de suas contas " "existentes. Se isso foi um erro, por favor, continue para fazer login." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "A conta de terceiros foi conectada." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "A conta de terceiros foi desconectada." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Você está prestes a usar sua conta %(provider_name)s para fazer login em\n" "%(site_name)s. Como passo final, por favor, complete o formulário a seguir:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Ou use uma conta de terceiros" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Saiu de todas as outras sessões." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Iniciado em" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "Endereço IP" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Navegador" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Última visualização em" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Atual" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Sair de outras sessões" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Sessões de usuário" #: usersessions/models.py:94 msgid "session key" msgstr "chave da sessão" ================================================ FILE: allauth/locale/pt_PT/LC_MESSAGES/django.po ================================================ # Portuguese from Portugal translation for django-allauth. # Copyright (C) 2023 # This file is distributed under the same license as the django-allauth package. # # Translators: # Fábio Santos, 2012 # Emanuel Angelo , 2023. # msgid "" msgstr "" "Project-Id-Version: django-allauth\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-08-04 13:01+0000\n" "Last-Translator: ssantos \n" "Language-Team: Portuguese (Portugal) \n" "Language: pt_PT\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.13-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Esta conta está atualmente inativa." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Não pode remover o seu endereço de e-mail principal." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Este endereço de email já está associado a esta conta." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "" "O endereço de email e/ou a palavra-passe que especificou não estão corretos." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "" "O número de telefone e/ou a palavra-passe que especificou não estão corretos." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Já existe um utilizador registado com este endereço de email." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Por favor insira a sua palavra-passe atual." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Código errado." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Palavra-passe errada." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Chave inválida ou expirada." #: account/adapter.py:79 msgid "Invalid login." msgstr "Login inválido." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "O código para redefinir a palavra-passe era inválido." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Não pode adicionar mais do que %d endereços de email." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Já existe um utilizador registado com este número de telefone." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "" "Demasiadas tentativas falhadas para iniciar sessão. Tente novamente mais " "tarde." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "O endereço de email não está associado a nenhuma conta." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "O número de telefone não está associado a nenhuma conta." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "O seu endereço de email primário tem de ser verificado." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Esse nome de utilizador não pode ser utilizado. Por favor, use outro." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "" "O nome de utilizador e/ou a palavra-passe que especificou não estão corretos." #: account/adapter.py:98 msgid "Please select only one." msgstr "Por favor, selecione apenas um." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "O valor novo deve ser diferente do atual." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Seja paciente, está a enviar muitos requerimentos." #: account/adapter.py:826 msgid "Use your password" msgstr "Utilizar palavra-passe" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Use uma app autenticadora ou código" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Use uma chave de segurança" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "Marcado {email} como verificado." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Marcar {email} como verificado falhou." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Marcar endereços de e-mail selecionados como verificados" #: account/apps.py:11 msgid "Accounts" msgstr "Contas" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "Email" #: account/fields.py:19 msgid "Email address" msgstr "Endereço de email" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" "Digite um número de telefone incluindo o código do país (por exemplo, +351 " "para Portugal)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefone" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Tem que escrever a mesma palavra-passe em ambos os campos." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Palavra-passe" #: account/forms.py:67 msgid "Remember Me" msgstr "Lembrar-me" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Nome de utilizador" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Iniciar sessão" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Nome de utilizador, email ou telefone" #: account/forms.py:117 msgid "Username or email" msgstr "Nome de utilizador ou email" #: account/forms.py:119 msgid "Username or phone" msgstr "Nome de utilizador ou telefone" #: account/forms.py:121 msgid "Email or phone" msgstr "Email ou telefone" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Esqueceu-se da sua palavra-passe?" #: account/forms.py:287 msgid "Email (again)" msgstr "Email (novamente)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Confirmação do endereço de email" #: account/forms.py:302 msgid "Email (optional)" msgstr "Email (opcional)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Nome de utilizador (opcional)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "deve escrever o mesmo endereço de email em ambos os campos." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Palavra-passe (novamente)" #: account/forms.py:591 msgid "Current Password" msgstr "Palavra-passe atual" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Nova palavra-passe" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Nova palavra-passe (novamente)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Código" #: account/models.py:23 msgid "user" msgstr "utilizador" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "endereço de email" #: account/models.py:31 msgid "verified" msgstr "verificado" #: account/models.py:32 msgid "primary" msgstr "primário" #: account/models.py:38 msgid "email addresses" msgstr "endereços de email" #: account/models.py:142 msgid "created" msgstr "criado" #: account/models.py:143 msgid "sent" msgstr "enviado" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "chave" #: account/models.py:149 msgid "email confirmation" msgstr "confirmação de email" #: account/models.py:150 msgid "email confirmations" msgstr "confirmações de email" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Visualizar o seu ID de utilizador" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Ver o seu endereço de email" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Ver as suas informações básicas de perfil" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Conceder permissões" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Os caracteres universais não são permitidos a menos que 'Permitir caracteres " "universais em URIs' esteja ativado." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "O URI '{}' contém mais do que um carácter universal (*). Apenas um carácter " "universal por URI é permitido." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "" "Os caracteres universais só são permitidos na parte do nome de anfitrião do " "URI." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Código de autorização" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Código de dispositivo" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Credenciais de cliente" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Atualizar o token" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Confidencial" #: idp/oidc/models.py:44 msgid "Public" msgstr "Público" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "O(s) escopo(s) que o cliente pode solicitar. Fornecer um valor por linha, " "por exemplo: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Caso o cliente não especifique qualquer escopo, esses escopos predefinidos " "são usados. Forneça um valor por linha, por exemplo: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Uma lista de tipos de concessão permitidos. Forneça um valor por linha, por " "exemplo: author_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Uma lista de origens permitidas para pedidos cross-origin, uma por linha." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Permitir caracteres universais (*) em URIs de redirecionamento e origens " "CORS. Quando ativado, os URIs podem conter um único asterisco para " "corresponder a subdomínios." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Uma lista de tipos de resposta permitidos. Forneça um valor por linha, por " "exemplo: code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "cliente" #: idp/oidc/models.py:116 msgid "clients" msgstr "clientes" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Não pode adicionar um endereço de email a uma conta que esteja protegida por " "autenticação de dois factores." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Não pode desativar a autenticação de dois fatores." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Não pode gerar códigos de recuperação sem ter a autenticação de dois fatores " "ativada." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Não pode ativar a autenticação de dois factores enquanto não verificar o seu " "endereço de email." #: mfa/adapter.py:141 msgid "Master key" msgstr "Chave mestre" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Chave de backup" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Chave nº {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Códigos de recuperação" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "Autenticador TOTP" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Código do autenticador" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Sem palavra-passe" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Ativar a operação sem palavra-passe permite que faça login apenas com esta " "chave, mas impõe requisitos adicionais, como biometria ou proteção com PIN." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Já existe uma conta com este endereço de email. Por favor, inicie a sessão " "com essa conta e depois associe a sua conta %s." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Token inválido." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "A sua conta não tem uma palavra-passe definida." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "A sua conta não tem um endereço de email verificado." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Não pode desconectar a sua última conta de terceiros conectada." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "A conta de terceiros já está conectada a uma conta diferente." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Contas de redes sociais" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "fornecedor" #: socialaccount/models.py:53 msgid "provider ID" msgstr "ID do fornecedor" #: socialaccount/models.py:57 msgid "name" msgstr "nome" #: socialaccount/models.py:59 msgid "client id" msgstr "ID do cliente" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "ID da aplicação ou chave do consumidor" #: socialaccount/models.py:64 msgid "secret key" msgstr "chave secreta" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "segredo da API, segredo do cliente ou segredo do consumidor" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Chave" #: socialaccount/models.py:82 msgid "social application" msgstr "aplicação social" #: socialaccount/models.py:83 msgid "social applications" msgstr "aplicações sociais" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "último início de sessão" #: socialaccount/models.py:121 msgid "date joined" msgstr "data da inscrição" #: socialaccount/models.py:122 msgid "extra data" msgstr "dados extra" #: socialaccount/models.py:126 msgid "social account" msgstr "conta social" #: socialaccount/models.py:127 msgid "social accounts" msgstr "contas sociais" #: socialaccount/models.py:161 msgid "token" msgstr "código" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) ou código de acesso (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "código secreto" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) ou código de refrescamento (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "expira em" #: socialaccount/models.py:175 msgid "social application token" msgstr "código da aplicação social" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "códigos da aplicação social" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Dados de perfil inválidos" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Login" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Cancelar" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Resposta inválida ao obter o código de requisição de \"%s\". A resposta foi: " "%s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Resposta inválida ao obter o código de acesso de \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Nenhum código de requisição gravado para \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Nenhum código de acesso gravado para \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Sem acesso a recursos privados em \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Resposta inválida ao obter o código de requisição de \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Conta desabilitada" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Esta conta está desabilitada." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Enviamos um código para %(recipient)s. O código expira brevemente, então por " "favor insira-o logo." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Confirmar" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Solicitar novo código" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Confirmar acesso" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Por favor, reautentique para proteger a sua conta." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Opções alternativas" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Verificação de e-mail" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Insira o código de verificação de e-mail" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Usar um endereço de email diferente" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Iniciar sessão" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Insira o código de entrada" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Redefinição da palavra-passe" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Introduzir código de redefinição da palavra-passe" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Verificação de telefone" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Digite o código de verificação do telefone" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Use um número de telefone diferente" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Endereços de email" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Os endereços de email seguintes estão associados à sua conta:" #: templates/account/email.html:25 msgid "Verified" msgstr "Verificado" #: templates/account/email.html:29 msgid "Unverified" msgstr "Não verificado" #: templates/account/email.html:34 msgid "Primary" msgstr "Primário" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Torná-lo primário" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Re-enviar verificação" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Remover" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Adicionar endereço de email" #: templates/account/email.html:70 msgid "Add Email" msgstr "Adicionar email" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Deseja mesmo remover o endereço de email marcado?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Está a receber este email porque você, ou alguém,\n" "tentou criar uma conta usando o endereço de email:\n" "\n" "%(email)s\n" "\n" "No entanto já existe uma conta com este endereço de email.\n" "Caso se tenha esquecido disto, use o procedimento de recuperação\n" "de palavra-passe esquecida para recuperar a sua conta:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "A conta já existe" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Olá de %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Obrigado por usar %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "Recebe este e-mail porque a seguinte alteração foi feita à sua conta:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Se não reconhece esta alteração, por favor tome as devidas precauções de " "segurança imediatamente. A alteração na sua conta origina de:\n" "\n" "- Endereço IP: %(ip)s\n" "- Navegador: %(user_agent)s\n" "- Data: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "O seu e-mail foi alterado de %(from_email)s para %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "E-mail alterado" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "O seu e-mail foi confirmado." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Confirmação de e-mail" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Está a receber este email porque o utilizador %(user_display)s forneceu o " "seu endereço de email para criar uma conta em %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "O seu código de verificação de e-mail está listado abaixo. Por favor, insira-" "o na janela do seu navegador aberta." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Para confirmar que isto é correto, vá para %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Confirme o seu endereço de email" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "O endereço de e-mail %(deleted_email)s foi removido da sua conta." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "E-mail removido" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "O seu código de entrada está listado abaixo. Por favor, insira-o na janela " "do seu navegador aberta." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "Este e-mail pode ser ignorado com segurança se não iniciou esta ação." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Código de entrada" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "A sua palavra-passe foi alterada." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Palavra-passe alterada" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "O seu código de redefinição de palavra-passe está listado abaixo. Por favor, " "insira-o na janela do navegador aberto." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Código de redefinição da palavra-passe" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Está a receber este email porque você, ou alguém, pediu para redefinir a " "palavra-passe da sua conta.\n" "Pode ignorar este email com segurança, caso não tenha feito tal pedido. " "Clique na ligação abaixo para redefinir a sua palavra-passe." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Caso se tenha esquecido, o seu nome de utilizador é %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Email com pedido de redefinição da palavra-passe" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "A sua palavra-passe foi redefinida." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "A sua palavra-passe foi definida." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Palavra-passe definida" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Recebe este e-mail porque você, ou outra pessoa, tentou aceder uma conta com " "o e-mail %(email)s. No entanto, não temos nenhum registo de tal conta no " "nosso banco de dados." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Se foi você, pode inscrever-se para uma conta através da ligação abaixo." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Conta desconhecida" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Endereço de email" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "E-mail atual" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "A alterar para" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "A seu endereço de e-mail ainda aguarda uma verificação." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Cancelar alteração" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Alterar para" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Alterar email" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Confirmar endereço de email" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Por favor, confirme que %(email)s é um " "endereço de email do utilizador %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Não foi possível confirmar %(email)s porque já foi confirmado numa conta " "diferente." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Esta ligação de verificação de email expirou ou é inválida. Por favor, peça uma nova verificação de email.." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Se ainda não criou uma conta, então por favor %(link)sinscreva-" "se%(end_link)s primeiro." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Entrar com uma chave de segurança" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Envie-me um código de login" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Terminar sessão" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Tem certeza de que quer terminar a sessão?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Não pode remover o seu endereço de email primário (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Email de confirmação enviado para %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Confirmou %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Removeu o endereço de email %(email)s." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Iniciou a sessão como %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Terminou a sessão." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Um código de login foi enviado para %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Palavra-passe alterada com sucesso." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Palavra-passe definida com sucesso." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Um código de verificação foi enviado para %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Você verificou o número de telefone %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Endereço de email primário definido." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Alterar palavra-passe" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Esqueceu-se da sua palavra-passe?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Esqueceu-se da sua palavra-passe? Insira o seu endereço de email abaixo e " "enviar-lhe-emos um email para que a possa redefinir." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Redefinir a minha palavra-passe" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "Contacte-nos se tiver dificuldades em redefinir a sua palavra-passe." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Enviámos-lhe um email. Se o não tiver recebido, verifique a pasta do spam. " "Se não o encontrar, contacte-nos, se não o receber dentro dos próximos " "minutos." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Código errado" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "A ligação para redefinir a palavra-passe era inválida, provavelmente por já " "ter sido usada. Faça um novo pedido para " "redefinir a palavra-passe." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "A sua palavra-passe está alterada." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Definir palavra-passe" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Alterar telefone" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Telefone atual" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "O seu número de telefone continua pendente de verificação." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Alterar telefone" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Insira a sua palavra-passe:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Receberá um código especial para um login sem palavra-passe." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Solicitar código" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Outras opções de entrada" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Inscrição" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Inscrever-se" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Já tem uma conta? Então %(link)sinicie a sessão%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Cadastrar-se usando uma chave de segurança" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Cadastro com chave de segurança" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Outras opções" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "As inscrições estão fechadas" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Lamentamos, mas as inscrições estão fechadas." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Nota" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Já tem a sessão iniciada como %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Aviso:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Neste momento não tem qualquer endereço de email definido. Devia mesmo " "adicionar um endereço de email para que possa receber notificações, " "redefinir a sua palavra-passe, etc." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Verifique o seu endereço de email" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Enviámos-lhe um email para verificação. Siga a ligação fornecida para " "finalizar a inscrição. Se não encontrar o email de verificação na sua caixa " "de correio, verifique a pasta do spam. Contacte-nos se não receber o email " "de verificação nos próximos minutos." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Esta parte da aplicação requer que verifiquemos que\n" "você é quem diz ser. Para esse fim, pedimos que comprove\n" "ter a posse do seu endereço de email. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Enviámos-lhe um email para.\n" "verificação. Clique na ligação que está nesse email. Se não encontrar o " "email de verificação na sua caixa de entrada, verifique a sua pasta do spam. " "Caso contrário,\n" "contacte-nos se não o receber dentro dos próximos minutos." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Nota: ainda pode alterar o seu " "endereço de email." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Mensagens:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menu:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Ligações da conta" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Autenticação de dois componentes" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sessões" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Autorizar" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s quer aceder a sua conta %(site_name)s." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Introduzir código do dispositivo" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Digite o código exibido no seu dispositivo." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Continuar" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Confirmar dispositivo" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Por favor, confirme o código exibido no seu %(client_name)s para autorizar " "este dispositivo." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Rejeitar" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Dispositivo autorizado" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Você autorizou com sucesso o seu dispositivo %(client_name)s." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Dispositivo rejeitado" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "A autorização para o seu dispositivo %(client_name)s foi rejeitado." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Erro" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Manter sessão iniciada" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "A sua conta está protegida por autenticação de dois factores. Insira o " "código do autenticador:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Um novo conjunto de códigos de recuperação da autenticação de dois fatores " "foi gerado." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Novos códigos de recuperação gerados" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Autenticador habilitado." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "App autenticadora ativada" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Autenticador desabilitado." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "App autenticadora desativada" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Uma nova chave de segurança foi adicionada." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Chave de segurança adicionada" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Uma chave de segurança foi removida." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Chave de segurança removida" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Autenticador" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "A autenticação usando um autenticador está habilitada." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Não há um autenticador habilitado." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Desabilitar" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Habilitar" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Chaves de segurança" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Você adicionou %(count)s chave de segurança." msgstr[1] "Você adicionou %(count)s chaves de segurança." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Nenhuma chave de segurança foi adicionada." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Gerir" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Adicionar" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Códigos de recuperação" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Há %(unused_count)s código de recuperação disponível de um total de " "%(total_count)s." msgstr[1] "" "Há %(unused_count)s códigos de recuperação disponíveis de um total de " "%(total_count)s." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Não há códigos de recuperação definidos." #: templates/mfa/index.html:96 msgid "View" msgstr "Visualizar" #: templates/mfa/index.html:102 msgid "Download" msgstr "Descarregar" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Gerar" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Foi gerado um novo conjunto de códigos de recuperação." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Chave de segurança adicionada." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Chave de segurança removida." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Introduza um código do autenticador:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "" "Está prestes a gerar um novo conjunto de códigos de recuperação para a sua " "conta." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Esta ação vai invalidar os seus códigos existentes." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Tem a certeza?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Códigos não utilizados" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Descarregar códigos" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Gerar novos códigos" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Habilitar autenticador" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Para proteger a sua conta com autenticação de dois fatores, digitalize o " "código QR abaixo com o seu autenticador. Depois, insira o código de " "verificação gerado pela aplicação, em baixo." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Segredo do autenticador" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Pode guardar este segredo e utilizá-lo para reinstalar o seu autenticador " "numa data posterior." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Desabilitar o autenticador" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Está prestes a desabilitar a autenticação baseada num autenticador. Tem a " "certeza?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Confiar neste navegador?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Se optar por confiar neste navegador, não será solicitado um código de " "verificação na próxima vez que entrar." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Confiança por %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Não confiar" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Adicionar chave de segurança" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Remover chave de segurança" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Tem certeza de que deseja remover esta chave de segurança?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Uso" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Chave de segurança" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Chave de segurança" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Esta chave não indica se é uma chave de segurança." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Não especificado" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Adicionado em %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Último uso em %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Editar" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Editar chave de segurança" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Gravar" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Criar chave de segurança" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Está prestes a criar uma chave de segurança para a sua conta. Como pode " "adicionar chaves adicionais posteriormente, pode usar um nome descritivo " "para diferenciar as chaves." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Criar" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Esta funcionalidade requer JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Falha no login de terceiros" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "Ocorreu um erro ao tentar fazer login via a sua conta de terceiros." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "Pode iniciar a sessão usando uma das seguintes contas externas:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Atualmente, não tem contas de terceiros conectadas a esta conta." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Adicionar uma conta externa" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Uma conta de terceiros de %(provider)s foi conectada à sua conta." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Conta de terceiros conectada" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "Uma conta de terceiros de %(provider)s foi desconectada da sua conta." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Conta de terceiros desconectada" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Conectar %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Está prestes a conectar uma nova conta externa de %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Iniciar sessão via %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" "Está prestes a iniciar sessão usando uma conta externa de %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Início de sessão cancelado" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Você decidiu cancelar o início da sessão na nossa aplicação usando uma das " "suas contas existentes. Se o fez por engano, inicie a sessão." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "A conta de terceiros foi conectada." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "A conta de terceiros foi desconectada." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Está prestes a usar a sua conta %(provider_name)s para iniciar sessão em\n" "%(site_name)s. Para finalizar, por favor complete o seguinte formulário:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Ou use uma conta de terceiros" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Saiu de todas as outras sessões." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Iniciado em" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "Endereço IP" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Navegador" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Última visualização em" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Atual" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Sair de outras sessões" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Sessões de utilizador" #: usersessions/models.py:94 msgid "session key" msgstr "chave da sessão" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Ligações da conta" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "A palavra-passe tem que ter um mínimo de {0} caracteres." #, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Está a receber este email porque você, ou alguém, pediu uma palavra-passe " #~ "para a sua conta.\n" #~ "No entanto não temos qualquer registo de um utilizador com o endereço de " #~ "email\n" #~ "%(email)s na nossa base de dados.\n" #~ "\n" #~ "Este email pode ser ignorado em segurança se não fez tal pedido.\n" #~ "\n" #~ "Se foi você, pode criar uma conta usando a ligação abaixo." #~ msgid "The following email address is associated with your account:" #~ msgstr "O endereço de email seguinte está associado à sua conta:" #~ msgid "Change Email Address" #~ msgstr "Alterar endereço de email" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Por favor, inicie a sessão com uma\n" #~ "das suas contas externas. Ou então %(link)sinscreva-se\n" #~ "em %(site_name)s e inicie a sessão abaixo:" #~ msgid "or" #~ msgstr "ou" #~ msgid "" #~ "To safeguard the security of your account, please enter your password:" #~ msgstr "" #~ "Para salvaguardar a segurança da sua conta, insira a sua palavra-passe:" #~ msgid "OpenID Sign In" #~ msgstr "Iniciar sessão com OpenID" ================================================ FILE: allauth/locale/ro/LC_MESSAGES/django.po ================================================ # DJANGO-ALLAUTH. # Copyright (C) 2016 # This file is distributed under the same license as the django-allauth package. # # Translators: # Steve Kossouho , 2016. # Gilou , 2019 # msgid "" msgstr "" "Project-Id-Version: django-allauthrr\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2024-04-20 21:34+0200\n" "Last-Translator: Gilou \n" "Language-Team: \n" "Language: ro_RO\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n==0 || (n!=1 && n%100>=1 && " "n%100<=19) ? 1 : 2);\n" "X-Generator: Poedit 3.0\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Acest cont este in prezent inactiv." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Nu poți elimina adresa ta principală de e-mail." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Aceasta adresa de e-mail este deja asociata acestui cont." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Adresa de e-mail si / sau parola sunt incorecte." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Numărul de telefon și/sau parola specificate nu sunt corecte." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Un utilizator este deja inregistrat cu aceasta adresa de e-mail." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Te rugam tasteaza parola actuala." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Cod incorect." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Parolă incorectă." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Cheie invalidă sau expirată." #: account/adapter.py:79 msgid "Invalid login." msgstr "Autentificare invalidă." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Link-ul de resetare a parolei nu era valid." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Nu poti adauga mai mult de %d adrese de e-mail." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Un utilizator este deja înregistrat cu acest număr de telefon." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Prea multe incercări de conectare esuate. Incearca mai tarziu." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Adresa de e-mail nu este atribuită niciunui cont de utilizator." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Numărul de telefon nu este atribuit niciunui cont de utilizator." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "" "Adresa de e-mail trebuie mai intai confirmata inainte de a o seta ca adresa " "principala. Acceseaza link-ul din e-mailul de verificare." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "" "Numele de utilizator nu este disponibil. Te rugam alege alt nume de " "utilizator." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Numele de utilizator si / sau parola sunt incorecte." #: account/adapter.py:98 msgid "Please select only one." msgstr "Te rugăm selectează doar una." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Noua valoare trebuie să fie diferită de cea curentă." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Ai răbdare, trimiți prea multe cereri." #: account/adapter.py:826 msgid "Use your password" msgstr "Folosește-ți parola" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Folosește aplicația de autentificare sau codul" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Folosește o cheie de securitate" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "Adresa {email} a fost marcată ca verificată." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Nu s-a reușit marcarea adresei {email} ca verificată." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Marchează adresele de e-mail selectate ca verificate" #: account/apps.py:11 msgid "Accounts" msgstr "Conturi" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-mail" #: account/fields.py:19 msgid "Email address" msgstr "Adresa de e-mail" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" "Introdu un număr de telefon inclusiv codul de țară (de ex. +40 pentru " "România)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefon" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Trebuie sa tastezi aceeași parolă de fiecare data." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Parola" #: account/forms.py:67 msgid "Remember Me" msgstr "Tine-ma minte" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Nume de utilizator" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Autentificare" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Nume de utilizator, e-mail sau telefon" #: account/forms.py:117 msgid "Username or email" msgstr "Nume de utilizator sau e-mail" #: account/forms.py:119 msgid "Username or phone" msgstr "Nume de utilizator sau telefon" #: account/forms.py:121 msgid "Email or phone" msgstr "E-mail sau telefon" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Ai uitat parola?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-mail (confirma)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Confirmarea adresei de e-mail" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-mail (optional)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Nume de utilizator (opțional)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Trebuie sa tastezi aceeasi adresa de e-mail de fiecare data." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Parola (confirma)" #: account/forms.py:591 msgid "Current Password" msgstr "Parola actuala" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Parola noua" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Parola noua (confirma)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Cod" #: account/models.py:23 msgid "user" msgstr "utilizator" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "adresa de e-mail" #: account/models.py:31 msgid "verified" msgstr "verificata" #: account/models.py:32 msgid "primary" msgstr "principala" #: account/models.py:38 msgid "email addresses" msgstr "adrese de e-mail" #: account/models.py:142 msgid "created" msgstr "creata" #: account/models.py:143 msgid "sent" msgstr "trimis" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "cheie" #: account/models.py:149 msgid "email confirmation" msgstr "confirmare e-mail" #: account/models.py:150 msgid "email confirmations" msgstr "confirmari e-mail" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Vizualizează-ți ID-ul de utilizator" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Vizualizează-ți adresa de e-mail" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Vizualizează-ți informațiile de bază ale profilului" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Acordă permisiuni" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Caracterele wildcard nu sunt permise decât dacă opțiunea 'Permite caractere " "wildcard în URI' este activată." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI-ul '{}' conține mai mult de un caracter wildcard (*). Este permis un " "singur wildcard per URI." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "" "Caracterele wildcard sunt permise doar în partea de hostname a URI-ului." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Cod de autorizare" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Cod de dispozitiv" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Acreditări client" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Token de reîmprospătare" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Confidențial" #: idp/oidc/models.py:44 msgid "Public" msgstr "Public" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Domeniul(-urile) pe care clientul are voie să le solicite. Furnizează o " "valoare per linie, de ex.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "În cazul în care clientul nu specifică niciun domeniu, se folosesc aceste " "domenii implicite. Furnizează o valoare per linie, de ex.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "O listă de tipuri de acordare permise. Furnizează o valoare per linie, de " "ex.: authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "O listă de origini permise pentru cereri cross-origin, una per linie." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Permite caractere wildcard (*) în URI-urile de redirecționare și originile " "CORS. Când este activat, URI-urile pot conține un singur asterisc pentru a " "potrivi subdomenii." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "O listă de tipuri de răspuns permise. Furnizează o valoare per linie, de " "ex.: code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "client" #: idp/oidc/models.py:116 msgid "clients" msgstr "clienți" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Nu poți adăuga o adresă de e-mail la un cont protejat prin autentificarea cu " "doi factori." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Nu poți dezactiva autentificarea cu doi factori." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Nu poți genera coduri de recuperare fără a avea autentificarea cu doi " "factori activată." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Nu poți activa autentificarea cu doi factori până nu ți-ai verificat adresa " "de e-mail." #: mfa/adapter.py:141 msgid "Master key" msgstr "Cheie principală" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Cheie de rezervă" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Cheia nr. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Coduri de recuperare" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "Autentificator TOTP" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Cod de autentificare" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Fără parolă" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Activarea operării fără parolă îți permite să te conectezi folosind doar " "această cheie, dar impune cerințe suplimentare precum biometrie sau " "protecție PIN." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Există deja un cont cu această adresă de e-mail. Te rugăm să te conectezi " "mai întâi la acel cont, apoi să conectezi contul tău %s." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Token invalid." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Contul tau nu are o parola setata." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Contul tau nu are o adresa de e-mail confirmata." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Nu poți deconecta ultimul tău cont terț rămas." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Contul terț este deja conectat la un alt cont." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Retele de socializare" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "furnizor" #: socialaccount/models.py:53 msgid "provider ID" msgstr "ID furnizor" #: socialaccount/models.py:57 msgid "name" msgstr "nume" #: socialaccount/models.py:59 msgid "client id" msgstr "id client" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "ID-ul aplicatiei sau cheia consumatorului" #: socialaccount/models.py:64 msgid "secret key" msgstr "cheie secreta" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API secret, client secret sau consumator secret" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Cheie" #: socialaccount/models.py:82 msgid "social application" msgstr "aplicatie sociala" #: socialaccount/models.py:83 msgid "social applications" msgstr "aplicatii sociale" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "ultima logare" #: socialaccount/models.py:121 msgid "date joined" msgstr "data inscrierii" #: socialaccount/models.py:122 msgid "extra data" msgstr "date suplimentare" #: socialaccount/models.py:126 msgid "social account" msgstr "retea de socializare" #: socialaccount/models.py:127 msgid "social accounts" msgstr "retele de socializare" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) sau token de acces (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "token secret" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) token de reimprospatare (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "expira la" #: socialaccount/models.py:175 msgid "social application token" msgstr "token pentru aplicatia de socializare" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "token-uri pentru aplicatiile de socializare" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Date de profil invalide" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Autentificare" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Anulează" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Raspuns invalid la obtinerea token-ului de solicitare de la \"%s\". " "Raspunsul a fost: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Raspuns invalid la obtinerea token-ului de acces de la \"% s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Nu s-a salvat niciun token de solicitare pentru \"% s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Nu s-a salvat niciun token de acces pentru \"% s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Nu exista acces la resurse private la \"% s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Raspuns invalid la obtinerea token-ului de solicitare de la \"% s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Cont inactiv" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Acest cont este inactiv." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Am trimis un cod la %(recipient)s. Codul expiră în curând, așa că te rugăm " "să-l introduci cât mai repede." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Confirma" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Solicită un cod nou" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Confirmă accesul" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Te rugăm să te reautentifici pentru a-ți proteja contul." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Opțiuni alternative" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Verificare e-mail" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Introdu codul de verificare a e-mailului" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Folosește o altă adresă de e-mail" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Conecteaza-te" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Introdu codul de conectare" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Reseteaza parola" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Introdu codul de resetare a parolei" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Verificare telefon" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Introdu codul de verificare a telefonului" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Folosește un alt număr de telefon" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Adrese de e-mail" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Urmatoarele adrese de e-mail sunt asociate contului tau:" #: templates/account/email.html:25 msgid "Verified" msgstr "Verificata" #: templates/account/email.html:29 msgid "Unverified" msgstr "Neverificata" #: templates/account/email.html:34 msgid "Primary" msgstr "Principala" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Seteaza ca principala" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Retrimite e-mail verificare" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Elimina adresa" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Adauga adresa de e-mail" #: templates/account/email.html:70 msgid "Add Email" msgstr "Adauga e-mail" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Sigur doresti sa elimini aceasta adresa de e-mail?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Primești acest e-mail deoarece tu sau altcineva a încercat să înregistreze\n" "un cont folosind adresa de e-mail:\n" "\n" "%(email)s\n" "\n" "Totuși, un cont cu această adresă de e-mail există deja. În cazul în care " "ai\n" "uitat de acest lucru, te rugăm să folosești procedura de recuperare a " "parolei pentru\n" "a-ți recupera contul:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Contul există deja" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Salutari de la %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Iti multumim ca folosesti %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Primești acest e-mail deoarece următoarea modificare a fost efectuată asupra " "contului tău:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Dacă nu recunoști această modificare, te rugăm să iei măsuri de securitate " "imediat. Modificarea contului tău provine de la:\n" "\n" "- Adresă IP: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Data: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "E-mailul tău a fost schimbat de la %(from_email)s la %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "E-mail schimbat" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "E-mailul tău a fost confirmat." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Confirmare e-mail" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Primești acest e-mail deoarece utilizatorul %(user_display)s a folosit " "adresa ta de e-mail pentru a înregistra un cont pe %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Codul tău de verificare a e-mailului este listat mai jos. Te rugăm să-l " "introduci în fereastra de browser deschisă." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Pentru a confirma că este corect, accesează %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Te rugam confirma adresa de e-mail" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "Adresa de e-mail %(deleted_email)s a fost eliminată din contul tău." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "E-mail eliminat" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Codul tău de conectare este listat mai jos. Te rugăm să-l introduci în " "fereastra de browser deschisă." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Acest e-mail poate fi ignorat în siguranță dacă nu ai inițiat această " "acțiune." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Cod de conectare" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Parola ta a fost schimbată." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Parolă schimbată" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Codul tău de resetare a parolei este listat mai jos. Te rugăm să-l introduci " "în fereastra de browser deschisă." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Cod de resetare a parolei" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Primești acest e-mail deoarece tu sau altcineva a solicitat o resetare de " "parolă pentru contul tău.\n" "Dacă nu ai solicitat resetarea parolei, poți ignora acest e-mail în " "siguranță. Accesează linkul de mai jos pentru a-ți reseta parola." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "In cazul in care ai uitat, numele tau de utilizator este %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "E-mail pentru resetarea parolei" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Parola ta a fost resetată." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Parola ta a fost setată." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Parolă setată" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Primești acest e-mail deoarece tu sau altcineva a încercat să acceseze un " "cont cu e-mailul %(email)s. Totuși, nu avem nicio înregistrare a unui astfel " "de cont în baza noastră de date." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Dacă ai fost tu, te poți înregistra pentru un cont folosind linkul de mai " "jos." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Cont necunoscut" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Adresă de e-mail" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "E-mail curent" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Se schimbă la" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Adresa ta de e-mail este încă în așteptarea verificării." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Anulează schimbarea" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Schimbă la" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Schimbă e-mailul" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Confirma adresa de e-mail" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Te rugam confirma ca %(email)s este o " "adresa de e-mail a utilizatorului %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Nu se poate confirma %(email)s deoarece este deja confirmată de un alt cont." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Link-ul de confirmare a adresei de e-mail a expirat sau este invalid. Te " "rugam solicita un nou link de confirmare." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Daca nu ai un cont inca, te rugam %(link)sinregistreaza-te%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Conectează-te cu o cheie de acces" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Trimite-mi un cod de conectare" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Deconecteaza-te" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Esti sigur(a) ca vrei sa te deconectezi?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "" "Nu poti elimina adresa de e-mail (%(email)s). \n" "Aceasta este setata ca adresa principala de e-mail." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Confirma e-mailul trimis catre %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Ai confirmat %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Adresa de e-mail eliminata %(email)s." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Esti conectat(a) ca %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Te-ai deconectat." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Un cod de conectare a fost trimis la %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Parola a fost schimbata cu success." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Parola a fost setata cu success." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Un cod de verificare a fost trimis la %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Ai verificat numărul de telefon %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Adresa principala de e-mail a fost setata." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Schimba parola" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Ai uitat parola?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Ți-ai uitat parola? Introdu adresa de e-mail mai jos și îți vom trimite un e-" "mail care îți va permite să o resetezi." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Reseteaza parola" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Te rugam contacteaza-ne daca intampini dificultati in resetarea parolei." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Ți-am trimis un e-mail. Dacă nu l-ai primit, verifică folderul de spam. " "Contactează-ne dacă nu îl primești în câteva minute." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Token invalid" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Link-ul de resetare a parolei este invalid, posibil din cauza ca a fost deja " "folosit. Te rugam solicita un nou link de " "resetare a parolei." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Parola ta a fost schimbata cu succes." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Seteaza parola" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Schimbă telefonul" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Telefon curent" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Numărul tău de telefon este încă în așteptarea verificării." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Schimbă telefonul" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Introdu parola ta:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Vei primi un cod special pentru conectare fără parolă." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Solicită cod" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Alte opțiuni de conectare" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Inregistrare" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Inregistreaza-te" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Ai deja un cont? Te rugam %(link)sconecteaza-te%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Înregistrează-te cu o cheie de acces" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Înregistrare cu cheie de acces" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Alte opțiuni" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Inregistrare blocata" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Ne pare rau, posibilitatea inregistrarii este momentan blocata." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Nota" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Ești deja conectat ca %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Avertizare:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "În prezent nu ai configurată nicio adresă de e-mail. Ar trebui să adaugi o " "adresă de e-mail pentru a primi notificări, a-ți reseta parola, etc." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Verifica-ti adresa de e-mail" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Ți-am trimis un e-mail pentru verificare. Accesează linkul furnizat pentru a " "finaliza procesul de înregistrare. Dacă nu vezi e-mailul de verificare în " "inbox, verifică folderul de spam. Te rugăm să ne contactezi dacă nu primești " "e-mailul de verificare în câteva minute." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Această secțiune a site-ului necesită verificarea faptului că\n" "ești cine pretinzi a fi. În acest scop, îți cerem să\n" "verifici proprietatea asupra adresei tale de e-mail. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Ți-am trimis un e-mail pentru\n" "verificare. Te rugăm să accesezi linkul din e-mail. Dacă nu vezi e-mailul de " "verificare în inbox, verifică folderul de spam. Altfel\n" "contactează-ne dacă nu îl primești în câteva minute." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Nota: poti in continuare sa-ti " "schimbi adresa de e-mail." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Mesaje:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Meniu:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Conexiuni" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Autentificare cu doi factori" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sesiuni" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Autorizează" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s dorește acces la contul tău %(site_name)s." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Introdu codul dispozitivului" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Introdu codul afișat pe dispozitivul tău." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Continuă" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Confirmă dispozitivul" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Te rugăm confirmă codul afișat pe %(client_name)s pentru a autoriza acest " "dispozitiv." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Refuză" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Dispozitiv autorizat" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Ai autorizat cu succes dispozitivul tău %(client_name)s." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Dispozitiv refuzat" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Autorizarea pentru dispozitivul tău %(client_name)s a fost refuzată." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Eroare" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Rămâi conectat" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Contul tău este protejat prin autentificarea cu doi factori. Te rugăm " "introdu un cod de autentificare:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Un nou set de coduri de recuperare pentru autentificarea cu doi factori a " "fost generat." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Coduri de recuperare noi generate" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Aplicația de autentificare a fost activată." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Aplicație de autentificare activată" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Aplicația de autentificare a fost dezactivată." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Aplicație de autentificare dezactivată" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "O nouă cheie de securitate a fost adăugată." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Cheie de securitate adăugată" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "O cheie de securitate a fost eliminată." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Cheie de securitate eliminată" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Aplicație de autentificare" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Autentificarea prin aplicație de autentificare este activă." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Nicio aplicație de autentificare nu este activă." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Dezactivează" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Activează" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Chei de securitate" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Ați adăugat %(count)s cheie de securitate." msgstr[1] "Ați adăugat %(count)s chei de securitate." msgstr[2] "Ați adăugat %(count)s de chei de securitate." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Nu au fost adăugate chei de securitate." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Administrează" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Adaugă" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Coduri de recuperare" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Este %(unused_count)s din %(total_count)s coduri de recuperare disponibil." msgstr[1] "" "Sunt %(unused_count)s din %(total_count)s coduri de recuperare disponibile." msgstr[2] "" "Sunt %(unused_count)s din %(total_count)s de coduri de recuperare " "disponibile." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Nu sunt configurate coduri de recuperare." #: templates/mfa/index.html:96 msgid "View" msgstr "Vizualizează" #: templates/mfa/index.html:102 msgid "Download" msgstr "Descarcă" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Generează" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Un nou set de coduri de recuperare a fost generat." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Cheie de securitate adăugată." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Cheie de securitate eliminată." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Introdu un cod de autentificare:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "" "Ești pe cale să generezi un nou set de coduri de recuperare pentru contul " "tău." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Această acțiune va invalida codurile tale existente." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Ești sigur(ă)?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Coduri neutilizate" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Descarcă coduri" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Generează coduri noi" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Activează aplicația de autentificare" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Pentru a-ți proteja contul cu autentificarea cu doi factori, scanează codul " "QR de mai jos cu aplicația ta de autentificare. Apoi, introdu codul de " "verificare generat de aplicație." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Secret autentificator" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Poți stoca acest secret și să-l folosești pentru a reinstala aplicația de " "autentificare ulterior." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Dezactivează aplicația de autentificare" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Ești pe cale să dezactivezi autentificarea prin aplicație de autentificare. " "Ești sigur(ă)?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Ai încredere în acest browser?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Dacă alegi să ai încredere în acest browser, nu ți se va cere un cod de " "verificare data viitoare când te conectezi." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Ai încredere pentru %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Nu am încredere" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Adaugă cheie de securitate" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Elimină cheie de securitate" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Ești sigur(ă) că vrei să elimini această cheie de securitate?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Utilizare" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Cheie de acces" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Cheie de securitate" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Această cheie nu indică dacă este o cheie de acces." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Nespecificat" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Adăugată pe %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Utilizată ultima dată %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Editează" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Editează cheie de securitate" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Salvează" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Creează cheie de acces" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Ești pe cale să creezi o cheie de acces pentru contul tău. Deoarece poți " "adăuga chei suplimentare ulterior, poți folosi un nume descriptiv pentru a " "le diferenția." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Creează" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Această funcționalitate necesită JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Autentificarea prin cont terț a eșuat" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "A apărut o eroare la încercarea de autentificare prin contul tău terț." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Te poți conecta la contul tău folosind oricare dintre următoarele conturi " "terțe:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "În prezent nu ai niciun cont terț conectat la acest cont." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Adaugă un cont terț" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Un cont terț de la %(provider)s a fost conectat la contul tău." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Cont terț conectat" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "Un cont terț de la %(provider)s a fost deconectat de la contul tău." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Cont terț deconectat" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Conectează %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Ești pe cale să conectezi un nou cont terț de la %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Conectează-te prin %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "Ești pe cale să te conectezi folosind un cont terț de la %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Conectare anulata" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Ai decis sa anulezi procesul de conectare la contul tau. Daca ai anulat din " "greseala, te rugam acceseaza link-ul conecteaza-te " "la contul tau." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Contul terț a fost conectat." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Contul terț a fost deconectat." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Esti pe cale de a folosi contul %(provider_name)s pentru a te conecta la\n" "%(site_name)s. Pentru finalizarea procesului, te rugam completeaza urmatorul " "formular:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Sau folosește un cont terț" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Deconectat din toate celelalte sesiuni." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Începută la" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "Adresă IP" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Browser" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Ultima activitate" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Curent" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Deconectează celelalte sesiuni" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Sesiuni utilizator" #: usersessions/models.py:94 msgid "session key" msgstr "cheie sesiune" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Conexiuni" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Parola trebuie să aibă minimum {0} caractere." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Ai primit acest e-mail pentru ca tu sau altcineva a solicitat o resetare " #~ "de parola.\n" #~ "Daca nu ai fost tu cel care a solicitat resetarea parolei, te rugam sa " #~ "ignori acest e-mail.\n" #~ "Pentru resetarea parolei acceseaza linkul de mai jos." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "Urmatoarele adrese de e-mail sunt asociate contului tau:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Confirma adresa de e-mail" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Te rugam conecteaza-te cu unul\n" #~ "din conturile de mai jos. Sau %(link)screeaza un cont\n" #~ "%(site_name)s si apoi conecteaza-te folosind formularul de mai jos:" #~ msgid "or" #~ msgstr "sau" #~ msgid "change password" #~ msgstr "schimba parola" #~ msgid "OpenID Sign In" #~ msgstr "Conectare OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "Aceasta adresa de e-mail este deja asociata altui cont." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Ti-am trimis un e-mail. Daca nu-l gasesti in \"inbox\", te rugam verifica " #~ "si folderul \"spam\".\n" #~ "Te rugam contacteaza-ne daca nu-l primesti in cateva minute." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "L'identifiant ou le mot de passe sont incorrects." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "" #~ "Un pseudonyme ne peut contenir que des lettres, des chiffres, ainsi que " #~ "@/./+/-/_." #~ msgid "This username is already taken. Please choose another." #~ msgstr "Ce pseudonyme est déjà utilisé, merci d'en choisir un autre." #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "Vous avez confirmé que l'adresse e-mail de l'utilsateur %(user_display)s " #~ "est %(email)s." #~ msgid "Thanks for using our site!" #~ msgstr "Merci d'utiliser notre site !" ================================================ FILE: allauth/locale/ru/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-07-25 20:26+0200\n" "Last-Translator: Anastasia Goryacheva \n" "Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || " "(n%100>=11 && n%100<=14)? 2 : 3);\n" "X-Generator: Weblate 5.12-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Учетная запись неактивна." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Вы не можете удалить свой основной адрес электронной почты." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Этот адрес электронной почты уже связан с этой учетной записью." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Указанные вами адрес электронной почты и/или пароль неверны." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Номер телефона и/или пароль неверны." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Пользователь с таким адресом электронной почты уже зарегистрирован." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Пожалуйста, введите свой текущий пароль." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Неверный код." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Неверный пароль." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Недействительный или просроченный ключ." #: account/adapter.py:79 msgid "Invalid login." msgstr "Недействительный логин." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Код сброса пароля оказался недействительным." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Вы не можете добавить более %d адресов электронной почты." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Пользователь с таким номером телефона уже зарегистрирован." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "" "Слишком много неудачных попыток входа в систему. Повторите попытку позже." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Адрес электронной почты не закреплен ни за одной учетной записью." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Номер телефона не привязан ни к одной учетной записи пользователя." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Ваш основной адрес электронной почты должен быть подтвержден." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Такое имя пользователя не может быть использовано, выберите другое." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Имя пользователя и/или пароль неверны." #: account/adapter.py:98 msgid "Please select only one." msgstr "Пожалуйста, выберите только один вариант." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Новое значение должно отличаться от текущего." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Пожалуйста, подождите, вы отправляете слишком много запросов." #: account/adapter.py:826 msgid "Use your password" msgstr "Используйте ваш пароль" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Используйте приложение-аутентификатор или код" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Используйте ключ безопасности" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "Адрес {email} отмечен как подтверждённый." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Не удалось отметить {email} как подтверждённый." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Отметить выбранные адреса электронной почты как подтверждённые" #: account/apps.py:11 msgid "Accounts" msgstr "Аккаунты" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-mail" #: account/fields.py:19 msgid "Email address" msgstr "Адрес электронной почты" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" "Введите номер телефона, включая код страны (например, +7 для России или " "Казахстана)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Номер телефона" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Вы должны ввести одинаковый пароль дважды." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Пароль" #: account/forms.py:67 msgid "Remember Me" msgstr "Запомнить меня" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Имя пользователя" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Войти" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Имя пользователя, электронная почта или номер телефона" #: account/forms.py:117 msgid "Username or email" msgstr "Имя пользователя или e-mail" #: account/forms.py:119 msgid "Username or phone" msgstr "Имя пользователя или номер телефона" #: account/forms.py:121 msgid "Email or phone" msgstr "Электронная почта или номер телефона" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Забыли свой пароль?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-mail (ещё раз)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Подтверждение адреса электронной почты" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-mail (опционально)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Имя пользователя (опционально)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Вы должны ввести одинаковый e-mail дважды." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Пароль (ещё раз)" #: account/forms.py:591 msgid "Current Password" msgstr "Текущий пароль" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Новый пароль" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Новый пароль (ещё раз)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Код" #: account/models.py:23 msgid "user" msgstr "пользователь" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "адрес электронной почты" #: account/models.py:31 msgid "verified" msgstr "подтвержден" #: account/models.py:32 msgid "primary" msgstr "основной" #: account/models.py:38 msgid "email addresses" msgstr "адреса электронной почты" #: account/models.py:142 msgid "created" msgstr "создано" #: account/models.py:143 msgid "sent" msgstr "отправлено" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "ключ" #: account/models.py:149 msgid "email confirmation" msgstr "подтверждение email адреса" #: account/models.py:150 msgid "email confirmations" msgstr "подтверждения email адресов" #: headless/apps.py:7 msgid "Headless" msgstr "Безголовый" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Просмотреть ваш идентификатор пользователя" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Просмотреть ваш адрес электронной почты" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Просмотреть основную информацию вашего профиля" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Предоставить разрешения" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Подстановочные знаки (*) не разрешены, если не включена опция «Разрешить " "подстановочные знаки в URI»." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' содержит более одного подстановочного знака (*). Допускается только " "один подстановочный знак на URI." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Подстановочные знаки (*) допускаются только в части имени хоста URI." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Код авторизации" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Код устройства" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Учётные данные клиента" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Токен обновления" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Конфиденциальный" #: idp/oidc/models.py:44 msgid "Public" msgstr "Публичный" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Области (scopes), которые клиент имеет право запрашивать. Укажите одно " "значение на строку, например: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Если клиент не указал области действия, используются эти области по " "умолчанию. Укажите одно значение на строку, например: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Список разрешённых типов авторизации. Укажите одно значение на строку, " "например: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Список разрешённых источников для кросс-доменных запросов, по одному на " "строку." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Разрешить подстановочные знаки (*) в URI перенаправления и источниках CORS. " "При включении URI могут содержать одну звёздочку для совпадения с " "поддоменами." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Список разрешённых типов ответов. Укажите одно значение на строку, например: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "клиент" #: idp/oidc/models.py:116 msgid "clients" msgstr "клиенты" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Вы не можете добавить адрес электронной почты в учетную запись, защищенную " "двухфакторной аутентификацией." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Вы не можете отключить двухфакторную аутентификацию." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Вы не можете генерировать коды восстановления, если не включена " "двухфакторная аутентификация." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Вы не сможете активировать двухфакторную аутентификацию, пока не подтвердите " "свой адрес электронной почты." #: mfa/adapter.py:141 msgid "Master key" msgstr "Основной ключ" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Запасной ключ" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Ключ №{number}" #: mfa/apps.py:9 msgid "MFA" msgstr "Многофакторная аутентификация (MFA)" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Коды восстановления" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "Аутентификатор TOTP" #: mfa/models.py:26 msgid "WebAuthn" msgstr "Ключ WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Код аутентификатора" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Без пароля" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Включение режима работы без пароля позволяет вам войти в систему, используя " "только этот ключ, но предъявляет дополнительные требования, такие как защита " "биометрией или PIN-кодом." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Учетная запись с таким адресом электронной почты уже существует.Пожалуйста, " "сначала войдите в эту учетную запись, а затем подключите %s." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Недействительный токен." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Для вашего аккаунта не установлен пароль." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "В вашей учетной записи нет подтвержденного адреса электронной почты." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "" "Вы не можете отключить свою последнюю оставшуюся стороннюю учетную запись." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "" "Учетная запись стороннего сервиса уже подключена к другой учетной записи." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Аккаунты в социальных сетях" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "провайдер" #: socialaccount/models.py:53 msgid "provider ID" msgstr "провайдер ID" #: socialaccount/models.py:57 msgid "name" msgstr "имя" #: socialaccount/models.py:59 msgid "client id" msgstr "id клиента" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "ID приложения или ключ потребителя" #: socialaccount/models.py:64 msgid "secret key" msgstr "секретный ключ" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "Секретный ключ API, клиента или потребителя" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Ключ" #: socialaccount/models.py:82 msgid "social application" msgstr "социальное приложение" #: socialaccount/models.py:83 msgid "social applications" msgstr "социальные приложения" #: socialaccount/models.py:118 msgid "uid" msgstr "UID пользователя" #: socialaccount/models.py:120 msgid "last login" msgstr "дата последнего входа в систему" #: socialaccount/models.py:121 msgid "date joined" msgstr "дата регистрации" #: socialaccount/models.py:122 msgid "extra data" msgstr "дополнительные данные" #: socialaccount/models.py:126 msgid "social account" msgstr "аккаунт социальной сети" #: socialaccount/models.py:127 msgid "social accounts" msgstr "аккаунты социальных сетей" #: socialaccount/models.py:161 msgid "token" msgstr "токен" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) или access token (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "секретный токен" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) или refresh token (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "истекает" #: socialaccount/models.py:175 msgid "social application token" msgstr "токен социального приложения" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "токены социальных приложений" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Неверные данные профиля" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Вход" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Отмена" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "Неверный ответ во время получения запроса от \"%s\". Ответ был: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Неверный ответ при получении токена доступа от \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Нет сохраненного ключа запроса для \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Нет сохраненного ключа доступа для \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Доступ к ресурсам закрыт \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Неверный ответ во время получения запроса от \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Аккаунт неактивен" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Этот аккаунт неактивен." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Мы отправили код на адрес %(recipient)s. Срок действия кода скоро истечет, " "поэтому, пожалуйста, введите его как можно скорее." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Подтвердить" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Запросить новый код" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Подтвердите доступ" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "" "Пожалуйста, пройдите повторную аутентификацию, чтобы обезопасить свою " "учетную запись." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Альтернативные варианты" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Подтверждение электронной почты" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Введите код подтверждения электронной почты" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Использовать другой адрес электронной почты" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Войти" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Введите код для входа в систему" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Сброс пароля" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Введите код для сброса пароля" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Подтверждение номера телефона" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Введите код подтверждения номера телефона" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Использовать другой номер телефона" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Адреса электронной почты" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "С вашей учетной записью связаны следующие адреса электронной почты:" #: templates/account/email.html:25 msgid "Verified" msgstr "Подтвержден" #: templates/account/email.html:29 msgid "Unverified" msgstr "Не подтвержден" #: templates/account/email.html:34 msgid "Primary" msgstr "Основной" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Сделать основным" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Отправить подтверждение ещё раз" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Удалить" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Добавить адрес электронной почты" #: templates/account/email.html:70 msgid "Add Email" msgstr "Добавить E-mail" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Вы действительно хотите удалить выбранный адрес электронной почты?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Вы получили это письмо, потому что вы или кто-то другой пытались " "зарегистрировать учетную запись,\n" "используя адрес электронной почты:\n" "\n" "%(email)s\n" "\n" "Однако учетная запись с таким адресом электронной почты уже существует.\n" "В случае, если вы забыли об этом, воспользуйтесь процедурой \"Забыли " "пароль\",\n" "чтобы восстановить свою учетную запись:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Учетная запись уже существует" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Вас приветствует %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Благодарим вас за использование сайта «%(site_name)s!»\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Вы получили это письмо, потому что в вашей учетной записи были сделаны " "следующие изменения:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Если вы не заметили этого изменения, пожалуйста, немедленно примите " "надлежащие меры безопасности. Изменения в вашей учетной записи произошли с:\n" "\n" "- IP-адреса: %(ip)s\n" "- Браузера: %(user_agent)s\n" "- Дата: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "" "Ваш адрес электронной почты был изменен с %(from_email)s на %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Электронная почта изменена" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Ваш адрес электронной почты был подтвержден." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Подтверждение по электронной почте" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Вы получили это письмо, потому что пользователь %(user_display)s указал ваш " "адрес электронной почты для регистрации учетной записи на сайте " "%(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Ваш регистрационный код указан ниже. Пожалуйста, введите его в открытом окне " "вашего браузера." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Для подтверждения перейдите на %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Пожалуйста, подтвердите Ваш адрес электронной почты" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "" "Адрес электронной почты %(deleted_email)s был удалён из вашей учётной записи." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Email удалён" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Ваш регистрационный код указан ниже. Пожалуйста, введите его в открытом окне " "вашего браузера." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Это письмо можно спокойно проигнорировать, если вы не инициировали это " "действие." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Код для входа" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Ваш пароль изменён." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Пароль Изменён" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Ваш код для сброса пароля указан ниже. Пожалуйста, введите его в открытом " "окне вашего браузера." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Код для сброса пароля" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Вы получили это письмо, потому что вы или кто-то другой запросили сброс " "пароля для вашей учетной записи.\n" "Если это были не вы, просто проигнорируйте это письмо. Нажмите на ссылку " "ниже, чтобы сбросить пароль." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Если вы вдруг забыли, ваше имя пользователя: %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Письмо для сброса пароля" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Ваш пароль был сброшен." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Ваш пароль был установлен." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Пароль Установлен" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Вы получили это электронное письмо, потому что вы или кто-то другой пытался " "получить доступ к учетной записи с помощью электронной почты %(email)s. " "Однако в нашей базе данных нет никаких записей о такой учетной записи." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Если это были вы, вы можете зарегистрировать учетную запись, перейдя по " "ссылке ниже." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Неизвестный аккаунт" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Адрес электронной почты" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Текущий e-mail" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Изменить на" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Ваш адрес электронной почты ожидает подтверждения." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Отменить изменения" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Изменить на" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Изменить E-mail" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Подтвердите адрес электронной почты" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Пожалуйста, подтвердите %(email)s для " "пользователя %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Невозможно подтвердить %(email)s, потому что он уже прикреплен к другой " "учетной записи." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Ссылка некорректна или срок её действия истек. Пожалуйста, отправьте новый запрос на подтверждение электронной " "почты." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Если у вас ещё нет учётной записи, пожалуйста, сначала " "%(link)sзарегистрируйтесь%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Войти с ключом доступа" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Пришлите мне код для входа" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Выйти" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Вы уверены, что хотите выйти?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "" "Вы не можете удалить свой основной адрес электронной почты (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Подтверждающее письмо отправлено на %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Адрес %(email)s подтверждён." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Удален адрес электронной почты %(email)s." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Успешный вход под именем %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Вы вышли." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "" "Код для входа был отправлен по электронной почте на адрес %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Пароль успешно изменён." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Пароль успешно установлен." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Код подтверждения был отправлен на %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Вы только что подтвердили номер телефона %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Установлен основной адрес электронной почты." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Изменить пароль" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Забыли пароль?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Забыли свой пароль? Введите Ваш адрес электронной почты ниже, и мы отправим " "Вам письмо, чтобы вы могли его сбросить." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Сбросить мой пароль" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "Свяжитесь с нами, если у вас возникли сложности со сменой пароля." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Мы отправили вам письмо. Если вы его не получили, проверьте папку \"Спам\". " "Свяжитесь с нами, если вы не получили письмо в течение нескольких минут." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Неправильный ключ" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Ссылка на сброс пароля неверна, вероятно, она уже была использована. Для " "нового сброса пароля перейдите по ссылке." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Ваш пароль изменён." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Установить пароль" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Изменить номер телефона" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Текущий номер телефона" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Ваш номер телефона по-прежнему ожидает подтверждения." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Изменить номер телефона" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Введите пароль:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Вы получите специальный код для входа в систему без пароля." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Код запроса" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Другие варианты входа" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Регистрация" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Зарегистрируйтесь" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Уже зарегистрированы? %(link)sВойдите%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Зарегистрироваться, используя ключ доступа" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Регистрация с ключом доступа" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Другие варианты" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Регистрация закрыта" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Мы сожалеем, но в текущий момент регистрация закрыта." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Заметка" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Вы уже вошли как %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Внимание:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Сейчас у вас нет прикрепленного адрес электронной почты. Рекомендуем " "добавить, чтобы иметь возможность получать уведомления, сбрасывать пароль и " "и т.д." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Подтвердите Ваш адрес электронной почты" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Мы отправили вам письмо с подтверждением. Перейдите по указанной ссылке, " "чтобы завершить процесс регистрации. Если вы не видите письмо с " "подтверждением в вашем основном почтовом ящике, проверьте папку " "\"Спам\".Пожалуйста, свяжитесь с нами, если вы не получите письмо с " "подтверждением в течение нескольких минут." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Эта часть сайта требует от нас подтверждения того, что\n" "вы являетесь тем, за кого себя выдаете. Для этого нам необходимо\n" "подтвердить собственность вашего адреса электронной почты. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Мы отправили вам письмо\n" "для проверки. Пожалуйста, перейдите по ссылке. Если вы не видите письмо с " "подтверждением в вашем основном почтовом ящике, проверьте папку \"Спам\".\n" "В противном случае свяжитесь с нами, если вы не получите его в течение " "нескольких минут." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Заметка: вы можете изменить свой " "адрес электронной почты." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Сообщения:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Меню:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Прикрепленные аккаунты" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Двухфакторная аутентификация" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Сеансы" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Авторизовать" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "" "%(client_name)s хочет получить доступ к вашей учётной записи %(site_name)s." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Введите код устройства" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Введите код, отображаемый на вашем устройстве." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Продолжить" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Подтвердить устройство" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Пожалуйста, подтвердите код, показанный на вашем %(client_name)s, чтобы " "авторизовать это устройство." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Отклонить" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Устройство авторизовано" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Вы успешно авторизовали ваше устройство %(client_name)s." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Устройство отклонено" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Авторизация для вашего устройства %(client_name)s была отклонена." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Ошибка" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Оставаться в системе" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Ваша учетная запись защищена двухфакторной аутентификацией. Пожалуйста, " "введите код аутентификатора:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Сгенерирован новый набор кодов восстановления двухфакторной аутентификации." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Сгенерированы Новые Коды Восстановления" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Активировано приложение Аутентификатор." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Активировано приложение для проверки подлинности" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Приложение Аутентификатор деактивировано." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Приложение для проверки подлинности отключено" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Был создан новый ключ доступа." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Создан ключ доступа" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Ключ доступа был удален." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Ключ доступа удален" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Приложение аутентификатор" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Аутентификация с помощью приложения-аутентификатора активна." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Приложение аутентификатора неактивно." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Деактивировать" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Активировать" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Ключи доступа" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Вы создали %(count)s ключ доступа." msgstr[1] "Вы создали %(count)s клюей доступа." msgstr[2] "Вы создали %(count)s ключей доступа." msgstr[3] "Вы создали %(count)s ключ доступа." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Ключи доступа ещё не были созданы." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Управлять" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Добавить" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Коды восстановления" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "Доступно %(unused_count)s из %(total_count)s кодов восстановления." msgstr[1] "Доступнен %(unused_count)s из %(total_count)s кодов восстановления." msgstr[2] "Доступны %(unused_count)s из %(total_count)s кодов восстановления." msgstr[3] "Доступны %(unused_count)s из %(total_count)s кодов восстановления." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Коды восстановления не установлены." #: templates/mfa/index.html:96 msgid "View" msgstr "Посмотреть" #: templates/mfa/index.html:102 msgid "Download" msgstr "Скачать" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Генерировать" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Был создан новый набор кодов восстановления." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Ключ доступа создан." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Ключ доступа удален." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Введите код аутентификатора:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "" "Вы собираетесь сгенерировать новый набор кодов восстановления для вашей " "учетной записи." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Это действие аннулирует существующие коды." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Вы уверены?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Неиспользуемые коды" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Скачать коды" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Генерировать новые коды" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Активировать приложение Аутентификатор" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Чтобы защитить свою учетную запись с помощью двухфакторной аутентификации, " "отсканируйте приведенный ниже QR-код с помощью приложения-аутентификатора. " "Затем введите проверочный код, сгенерированный приложением ниже." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Код аутентификатора" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Вы можете сохранить этот секрет ключ и использовать его для повторной " "установки приложения аутентификатора в будущем." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Отключите приложение Аутентификатор" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Вы собираетесь отключить аутентификацию с использованием приложения " "Аутентификатор. Вы уверены?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Доверять этому браузеру?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Если вы решите доверять этому браузеру, у вас не будут запрашивать " "проверочный код при следующем входе в систему." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Доверять в течение %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Не доверять" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Создать ключ доступа" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Удалить ключ доступа" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Вы уверены, что хотите удалить этот ключ доступа?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Использование" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Ключ доступа (passkey)" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Ключ безопасности (Security key)" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Этот ключ не указывает, является ли он паролем доступа." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Не указано" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Добавлено %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Последний раз использовалось %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Редактировать" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Редактировать ключ безопасности" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Сохранить" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Создать ключ доступа (passkey)" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Вы собираетесь создать ключ доступа (passkey) для своей учётной записи. " "Поскольку вы сможете добавить дополнительные ключи позже, рекомендуется " "использовать описательное название, чтобы различать их." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Создать" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Для этой функции требуется JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Ошибка входа через внешний сервис" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "При попытке войти через учетную запись стороннего сервиса произошла ошибка." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "Вы можете авторизоваться, используя следующие сервисы:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "" "В настоящее время у вас нет учетных записей сторонних сервисов, связанных с " "этой учетной записью." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Добавить внешний аккаунт" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "" "К вашей учетной записи была подключена сторонняя учетная запись от " "%(provider)s." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Подключена учетная запись стороннего сервиса" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" "Сторонняя учетная запись от %(provider)s была отключена от вашей учетной " "записи." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Учетная запись стороннего сервиса отключена" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Соединение с %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Вы собираетесь подключить новый сторонний аккаунт из %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Вход через %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" "Вы собираетесь войти, используя стороннюю учетную запись из %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Авторизация отменена" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Вы прервали авторизацию, используя один из ваших аккаунтов. Если это было " "ошибкой, перейдите к авторизации." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Учетная запись стороннего сервиса подключена." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Сторонняя учетная запись была отключена." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Вы используете %(provider_name)s для авторизации на \n" "%(site_name)s. Чтобы завершить, заполните следующую форму:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Или использовать сторонний" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Выйти из всех остальных сеансов." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Начато в" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP-адрес" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Браузер" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Последний вход в" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Текущий" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Выйти из других сеансов" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Сеансы пользователей" #: usersessions/models.py:94 msgid "session key" msgstr "ключ сеанса" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Прикрепленные аккаунты" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Минимальное количество символов в пароле: {0}." #, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Вы получили это письмо, потому что вы или кто-то другой запросили сброс " #~ "пароля для вашей учетной записи.Однако нашей базе данных ничего не " #~ "известно о пользователес электронной почтой %(email)s.\n" #~ "\n" #~ "Если это были не вы, просто проигнорируйте это письмо.\n" #~ "Если же это всё-таки были вы, вы можете зарегистрировать аккаунт по " #~ "ссылке ниже." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "Следующие e-mail адреса прикреплены к вашему аккаунту:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Подтвердите e-mail адрес." #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Пожалуйста, войдите с одним\n" #~ "из ваших внешних аккаунтов. Или зарегистрируйтесь\n" #~ "и авторизуйтесь на сайте %(site_name)s:" #~ msgid "or" #~ msgstr "или" #~ msgid "change password" #~ msgstr "изменить пароль" #~ msgid "OpenID Sign In" #~ msgstr "Войти с OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "Указанный e-mail прикреплен к другому пользователю." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Мы отправили вам письмо. Пожалуйста, свяжитесь с нами, если не получили " #~ "его в течение нескольких минут." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "Логин и/или пароль не верны." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "Имя пользователя может включать буквы, цифры и @/./+/-/_." #~ msgid "This username is already taken. Please choose another." #~ msgstr "" #~ "Такое имя пользователя уже используется на сайте. Пожалуйста выберите " #~ "другое." #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "Вы подтвердили адрес %(email)s для " #~ "пользователя %(user_display)s." #~ msgid "Thanks for using our site!" #~ msgstr "Спасибо за использование нашего сайта!" #~ msgid "Confirmation email sent to %(email)s" #~ msgstr "Подтверждение выслано на %(email)s" #~ msgid "Delete Password" #~ msgstr "Удалить пароль" #~ msgid "" #~ "You may delete your password since you are currently logged in using " #~ "OpenID." #~ msgstr "Вы можете удалить свой пароль, при использовании OpenID." #~ msgid "delete my password" #~ msgstr "удалите мой пароль" #~ msgid "Password Deleted" #~ msgstr "Пароль удалён" ================================================ FILE: allauth/locale/sk/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-07-25 20:26+0200\n" "Last-Translator: Adam Zahradník \n" "Language-Team: Slovak \n" "Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n " ">= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n" "X-Generator: Weblate 5.13-dev\n" "X-Translated-Using: django-rosetta 0.9.9\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Tento účet nie je momentálne aktívny." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Primárna e-mailová adresa sa nedá odstrániť." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Táto e-mailová adresa je už spojená s týmto účtom." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Uvedený e-mail alebo heslo nie je správne." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Uvedené telefónne číslo alebo heslo nie je správne." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Používateľ s touto e-mailovou adresou už existuje." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Prosím, napíšte svoje súčasné heslo." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Nesprávny kód." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Nesprávne heslo." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Neplatný kľúč." #: account/adapter.py:79 msgid "Invalid login." msgstr "Neplatné prihlásenie." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Token na obnovu hesla bol nesprávny." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Nemôžte pridať viac než %d e-mailových adries." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Používateľ s týmto telefónnym číslom už existuje." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Príliš veľa neúspešných pokusov o prihlásenie. Skúste to znova neskôr." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "" "Táto e-mailová adresa nie je pridelená k žiadnemu používateľskému kontu." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "" "Toto telefónne číslo nie je pridelené k žiadnemu používateľskému kontu." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Primárna e-mailová adresa musí byť overená." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Používateľské meno nemôže byť použité. Použite prosím iné meno." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Uvedené používateľské meno alebo heslo nie je správne." #: account/adapter.py:98 msgid "Please select only one." msgstr "Prosím, zvoľte iba jednu možnosť." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Nová hodnota sa musí líšiť od aktuálnej hodnoty." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Buďte trpezliví, posielate príliš veľa požiadaviek." #: account/adapter.py:826 msgid "Use your password" msgstr "Pokračovať s heslom" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Pokračovať s autentifikačnou aplikáciou alebo kódom" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Pokračovať s bezpečnostným kľučom" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "E-mail {email} bol overený." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Nepodarilo sa overiť e-mail {email}." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Označiť vybrané adresy ako overené" #: account/apps.py:11 msgid "Accounts" msgstr "Účty" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-mail" #: account/fields.py:19 msgid "Email address" msgstr "E-mailová adresa" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Zadajte telefónne číslo vrátane predvoľby (t.j. s +421)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefón" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Heslá sa nezhodujú." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Heslo" #: account/forms.py:67 msgid "Remember Me" msgstr "Zapamätať si ma" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Používateľské meno" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Login" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Požívateľské meno, e-mail alebo telefónne číslo" #: account/forms.py:117 msgid "Username or email" msgstr "Používateľské meno alebo e-mail" #: account/forms.py:119 msgid "Username or phone" msgstr "Používateľské meno alebo telefónne číslo" #: account/forms.py:121 msgid "Email or phone" msgstr "E-mail alebo telefónne číslo" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Zabudnuté heslo?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-mail (znova)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Potvrdenie e-mailu" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-mail (nepovinné)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Používateľské meno (nepovinné)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "E-maily sa nezhodujú." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Heslo (znovu)" #: account/forms.py:591 msgid "Current Password" msgstr "Súčasné heslo" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Nové heslo" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Nové heslo (znovu)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Kód" #: account/models.py:23 msgid "user" msgstr "používateľ" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "e-mailová adresa" #: account/models.py:31 msgid "verified" msgstr "overený" #: account/models.py:32 msgid "primary" msgstr "primárny" #: account/models.py:38 msgid "email addresses" msgstr "e-mailové adresy" #: account/models.py:142 msgid "created" msgstr "vytvorený" #: account/models.py:143 msgid "sent" msgstr "odoslané" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "kľúč" #: account/models.py:149 msgid "email confirmation" msgstr "potvrdenie e-mailu" #: account/models.py:150 msgid "email confirmations" msgstr "potvrdenia e-mailu" #: headless/apps.py:7 msgid "Headless" msgstr "Aplikácia bez rozhrania (headless)" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Zobraziť identifikátor používateľa" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Zobraziť e-mailovú adresu" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Zobraziť základné osobné údaje" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Povolené oprávnenia" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Zástupné znaky nie sú povolené, pokiaľ nie je zapnutá možnosť 'Povoliť " "zástupné znaky v URI'." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' obsahuje viac ako jeden zástupný znak (*). Na jednu URI je povolený " "iba jeden zástupný znak." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Zástupné znaky sú povolené iba v časti s názvom hostiteľa v URI." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Autorizačný kód" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Kód zariadenia" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Prihlasovacie údaje klienta" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Obnovovací token" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Dôverný klient" #: idp/oidc/models.py:44 msgid "Public" msgstr "Verejný klient" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Oprávnenia, ktoré bude mať klient k dispozícií. Uveďte jednu hodnotu na " "riadok, napr.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Ak si klient nevyžiada žiadne oprávnenia, použijú sa tieto predvolené " "oprávnenia. Uveďte jednu hodnotu na riadok, napr.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Zoznam povolených autorizačných tokov. Uveďte jeden na riadok, napr.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "Povolené domény pre prichádzajúce požiadavky, jedna na riadok." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Povoliť zástupné znaky (*) v presmerovacích URI a CORS pôvodoch. Keď je táto " "možnosť zapnutá, URI môžu obsahovať jednu hviezdičku na zhodu subdomén." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Zoznam povolených typov odpovedí. Uveďte jednu hodnotu na riadok, napr.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "klient" #: idp/oidc/models.py:116 msgid "clients" msgstr "klienti" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Nemôžete si pridať ďalšiu e-mailovú adresu do účtu s dvojfaktorovou " "autentifikáciou." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Nemôžete si deaktivovať dvojfaktorovú autentifikáciu." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Nemôžete si vygenerovať záložné kódy bez aktívnej dvojfaktorovej " "autentifikácie." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Nemôžete si aktivovať dvojfaktorovú autentifikáciu pokiaľ nemáte overenú " "svoju adresu." #: mfa/adapter.py:141 msgid "Master key" msgstr "Hlavný kľúč" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Záložný kľúč" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Kľúč č. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Záložné kódy" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP autentifikátor" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Kód z autentifikátora" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Prihlásenie bez hesla" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Aktivácia prihlásenia bez hesla vám dovolí prihlásiť sa len pomocou tohto " "kľúča, ale vyžaduje dodatočné požiadavky ako napríklad PIN alebo biometriu." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Účet s touto e-mailovou adresou už existuje. Prosím, prihláste sa najprv pod " "daným účtom a potom pripojte svoj %s účet." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Neplatný token." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Váš účet nemá nastavené heslo." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Váš účet nemá overenú e-mailovú adresu." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Nemôžete odstrániť posledný externý účet." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Tento externý účet je už používa iný používateľ." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Účty na sociálnych sieťach" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "poskytovateľ" #: socialaccount/models.py:53 msgid "provider ID" msgstr "ID poskytovateľa" #: socialaccount/models.py:57 msgid "name" msgstr "meno" #: socialaccount/models.py:59 msgid "client id" msgstr "identifikátor klienta" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "ID aplikácie alebo zákaznícky kľúč" #: socialaccount/models.py:64 msgid "secret key" msgstr "tajný kľúč" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "Kľúč API, klienta alebo zákazníka" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Kľúč" #: socialaccount/models.py:82 msgid "social application" msgstr "sociálna aplikácia" #: socialaccount/models.py:83 msgid "social applications" msgstr "sociálne aplikácie" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "posledné prihlásenie" #: socialaccount/models.py:121 msgid "date joined" msgstr "dátum pripojenia" #: socialaccount/models.py:122 msgid "extra data" msgstr "ďalšie údaje" #: socialaccount/models.py:126 msgid "social account" msgstr "sociálny účet" #: socialaccount/models.py:127 msgid "social accounts" msgstr "sociálne účty" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) alebo prístupvý token (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "heslo prístupového tokenu" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) alebo token obnovenia (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "expiruje" #: socialaccount/models.py:175 msgid "social application token" msgstr "token sociálnej aplikácie" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "tokeny sociálnej aplikácie" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Nesprávne profilové údaje" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Prihlásiť sa" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Zrušiť" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "Neplatná odpoveď pri získavaní request tokenu z \"%s\". Odpoveď: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Neplatná odpoveď pri získavaní prístupového tokenu z \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Žiadna uložená požiadavka tokenu pre \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Žiadny uložený prístupový token pre \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Žiadny prístup do privátneho úložiska na \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Neplatná odpoveď pri získavaní request tokenu z \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Účet neaktívny" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Tento účet je neaktívny." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Odoslali sme overovací kód na e-mail %(recipient)s. Platnosť tohto kódu " "čoskoro vyprší, zadajte ho čo najskôr." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Potvrdiť" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Vyžiadať nový kód" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Potvrdiť prístup" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Znova sa overte, aby ste ochránili svoj účet." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Alternatívne možnosti" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Overenie e-mailovej adresy" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Zadajte kód na overenie e-mailovej adresy" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Použite inú e-mailovú adresu" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Prihlásiť sa" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Zadanie prihlasovacieho kódu" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Obnoviť heslo" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Zadanie kódu na obnovu hesla" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Overenie telefónneho čísla" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Zadanie kódu na overenie telefónneho čísla" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Použite iné telefónne číslo" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "E-mailová adresa" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Nasledujúce e-mailové adresy sú prepojené s vašim účtom:" #: templates/account/email.html:25 msgid "Verified" msgstr "Overený" #: templates/account/email.html:29 msgid "Unverified" msgstr "Neoverený" #: templates/account/email.html:34 msgid "Primary" msgstr "Primárny" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Označiť ako primárny" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Opätovne zaslať overenie" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Odstrániť" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Pridať e-mailovú adresu" #: templates/account/email.html:70 msgid "Add Email" msgstr "Pridať e-mail" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Naozaj chcete odstrániť vybranú e-mailovú adresu?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Tento e-mail ste dostali, pretože ste sa vy, alebo niekto iný\n" "pokúšali vytvoriť účet pre e-mailovu adresu:\n" "\n" "%(email)s\n" "\n" "Účet s touto e-mailovou adresou už však existuje. V prípade,\n" "že ste na svoju registráciu zabudli, použite prosím funkciu obnovy\n" "hesla vášho účtu:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Účet už existuje" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Dobrý deň z %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Ďakujeme za využitie %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "Tento e-mail dostávate kvôli nasledujúcim zmenám vo vašom účte:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Ak ste tieto zmeny nevykonali vy, prosím okamžite prijmite náležité " "bezpečnostné opatrenia. Zmeny vo vašom účte pochádzajú z:\n" "\n" "- IP adresa: %(ip)s\n" "- Prehliadač: %(user_agent)s\n" "- Dátum: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Vaša e-mailová adresa bola zmenená z %(from_email)s na %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "E-mailová adresa zmenená" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Vaša e-mailová adresa bola overená." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Overenie e-mailovej adresy" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Tento e-mail dostávate, pretože používateľ %(user_display)s zadal vašu " "emailovú adresu pri registrácii účtu na %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Váš kód na overenie e-mailovej adresy je uvedený nižšie. Prosím, zadajte ho " "do otvoreného okna vo vašom prehliadači." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Pre overenie správnosti navštívte %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Potvrďte prosím svoju e-mailovú adresu" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "E-mailová adresa %(deleted_email)s bola odstránená z vášho účtu." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "E-mailová adresa odstránená" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Váš prihlasovací kód je uvedený nižšie. Prosím, zadajte ho do otvoreného " "okna vo vašom prehliadači." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Tento e-mail môžete kľudne ignorovať, ak ste si nevyžiadali túto akciu." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Prihlasovací kód" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Vaše heslo bolo zmenené." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Heslo zmenené" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Váš kód na obnovu hesla je uvedený nižšie. Prosím, zadajte ho do otvoreného " "okna vo vašom prehliadači." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Kód na obnovu hesla" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Tento e-mail ste dostali, pretože niekto požiadal o heslo k vášmu " "používateľskému účtu.\n" "Ak ste to neboli vy, túto správu môžete pokojne ignorovať. Kliknutím na " "odkaz nižšie obnovíte svoje heslo." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Ak ste náhodou zabudli, vaše používateľské meno je %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "E-mail pre obnovu hesla" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Vaše heslo bolo obnovené." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Vaše heslo bolo nastavené." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Heslo nastavené" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Tento e-mail dostávate, pretože ste sa vy, alebo niekto iný pokúsili " "pristúpiť k účtu s e-mailovou adresou %(email)s. Avšak, neevidujeme žiaden " "účet s danou adresou v našom systéme." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Ak ste to boli vy, môžete si zaregistrovať nový účet navštívením linku " "nižšie." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Neznámy účet" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "E-mailová adresa" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Súčasná e-mailová adresa" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Zmeniť na" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Vaša e-mailová adresa stále čaká na overenie." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Zrušiť zmenu" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Zmeniť na" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Zmeniť e-mail" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Potvrdiť e-mailovú adresu" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Prosím potvrďte, že %(email)s je e-mailová " "adresa pre používateľa %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "Nemožno potvrdiť %(email)s, pretože už je potvrdený iným používateľom." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Odkaz na potvrdenie e-mailu je neplatný alebo vypršal. Zaslať novú žiadosť o overovací e-mail." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Ak ešte nemáte vytvorený účet, tak sa prosím najprv " "%(link)szaregistrujte%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Prihlásiť sa prístupovým kľúčom" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Poslať prihlasovací kód" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Odhlásiť" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Naozaj sa chcete odhlásiť?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Primárna e-mailová adresa sa nedá odstrániť (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Overovací e-mail poslaný na %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Adresa %(email)s potvrdená." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "E-mailová adresa %(email)s bola úspešne odstránená." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Úspešne ste sa prihlásili ako %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Odhlásili ste sa." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Prihlasovací kód bol odoslaný na %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Zmena hesla prebehla úspešne." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr ")Nastavenie hesla bolo úspešné." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Overovací kód bol odoslaný na %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Telefónne číslo %(phone)s bolo overené." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Primárna e-mailová adresa bola úspešne zadaná." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Zmeniť heslo" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Zabudnuté heslo?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Zabudli ste heslo? Vložte nižšie svoju e-mailovú adresu a čoskoro vám " "pošleme e-mail na obnovenie hesla." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Obnoviť moje heslo" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Prosím, kontaktujte nás, ak máte nejaký problém s obnovením svojho hesla." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Poslali sme vám e-mail. Ak ste ho nedostali, skontrolujte si priečinok " "nevyžiadanej pošty. Ak ho v priebehu pár minút neobdržíte, kontaktujte nás." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Zlý token" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Odkaz na obnovu hesla je neplatný, pravdepodobne už bol použitý. Vyžiadajte " "si nové obnovenie hesla." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Vaše heslo bolo zmenené." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Nastaviť heslo" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Zmeniť telefónne číslo" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Aktuálne telefónne číslo" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Vaše telefónne číslo stále čaká na overenie." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Zmeniť telefónne číslo" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Zadajte svoje heslo:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Obdržíte špeciálny kód, ktorý použijete na prihlásenie." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Vyžiadať kód" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Iné možnosti prihlásenia" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Registrácia" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Zaregistrovať" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Už ste sa zaregistrovali? Môžete sa %(link)sprihlásiť%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Zaregistrovať sa prístupovým kľúčom" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Registrácia pomocou prístupového kľúča" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Ďalšie možnosti" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Registrácia uzavretá" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Ospravedlňujeme sa, ale registrácia je momentálne uzavretá." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Poznámka" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Už ste prihlásený/á ako %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Varovanie:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Momentálne nemáte nastavený žiaden e-mail. Pridajte si e-mailovú adresu, aby " "ste mohli dostávať oznámenia, obnoviť zabudnuté heslo, atď." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Potvrďte e-mailovú adresu" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Poslali sme vám overovací e-mail. Kliknutím na uvedený odkaz dokončite " "proces registrácie. Ak ste ho nedostali, skontrolujte si priečinok so " "spamom. V prípade, že do niekoľkých minút nedostanete overovací e-mail, " "kontaktujte nás." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Na prezeranie nasledujúceho obsahu je potrebné overenie vašej e-mailovej " "adresy. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Poslali sme vám overovací e-mail. Kliknite prosím na odkaz v e-maili. Ak ste " "email neobdržali, skontrolujte priečinok nevyžiadanej pošty. V prípade, že " "ho do niekoľkých minút nedostanete, kontaktujte nás." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Poznámka: stále môžete zmeniť " "svoju e-mailovú adresu." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Správy:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menu:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Pripojenia účtu" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Dvojfaktorová autentifikácia" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Relácie" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Autorizovať" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s žiada prístup do vášho %(site_name)s účtu." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Zadanie kódu zo zariadenia" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Zadajte kód zobrazený na vašom zariadení." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Pokračovať" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Potvrdiť zariadenie" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Prosím, skontrolujte kód zobrazený na vašom %(client_name)s na autorizáciu " "tohto zariadenia." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Odmietnuť" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Zariadenie bolo autorizované" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Úspešne ste autorizovali vaše zariadenie %(client_name)s." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Zariadenie zamietnuté" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Autorizácia vášho zariadenia %(client_name)s bola odmietnutá." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Chyba" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Zostať prihlásený" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Váš účet je chránený dvojfaktorovu autentifikáciou. Prosím zadajte kód z " "autentifikátora:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "Boli vygenerované nové záložné kódy pre dvojfaktorovú autentifikáciu." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Nové záložné kódy" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Autentifikačná aplikácia aktivovaná." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Aktivácia autentifikačnej aplikácie" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Autentifikačná aplikácia deaktivovaná." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Deaktivácia autentifikačnej aplikácie" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Bol pridaný nový bezpečnostný kľúč." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Pridaný bezpečnostný kľúč" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Bezpečnostný kľúč bol odstránený." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Odstránený bezpečnostný kľúč" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Autentifikačná aplikácia" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Autentifikácia pomocou aplikácie je aktívna." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Autentifikačná aplikácia je neaktívna." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Deaktivovať" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Aktivovať" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Bezpečnostné kľúče" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Pridali ste si %(count)s bezpečnostný kľúč." msgstr[1] "Pridali ste si %(count)s bezpečnostné kľúče." msgstr[2] "Pridali ste si %(count)s bezpečnostných kľúčov." msgstr[3] "Pridali ste si %(count)s bezpečnostných kľúčov." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Nepridali ste si žiadne bezpečnostné kľúče." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Spravovať" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Pridať" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Záložné kódy" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Je k dispozícii %(unused_count)s kódov z celkového počtu %(total_count)s " "záložných kódov." msgstr[1] "" "Je k dispozícii %(unused_count)s kód z celkového počtu %(total_count)s " "záložných kódov." msgstr[2] "" "Je k dispozícii %(unused_count)s kódov z celkového počtu %(total_count)s " "záložných kódov." msgstr[3] "" "Je k dispozícii %(unused_count)s kódu z celkového počtu %(total_count)s " "záložných kódov." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Nie sú nastavené žiadne záložné kódy." #: templates/mfa/index.html:96 msgid "View" msgstr "Zobraziť" #: templates/mfa/index.html:102 msgid "Download" msgstr "Stiahnuť" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Generovať" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Boli vygenerované nové záložné kódy." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Bezpečnostný kľúč pridaný." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Bezpečnostný kľúč odstránený." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Zadajte kód z autentifikátora:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Chystáte sa vygenerovať nové záložné kódy pre váš účet." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Táto akcia zruší platnosť existujúcich kódov." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Ste si istý/á?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Nepoužité kódy" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Stiahnuť kódy" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Generovať nové kódy" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Aktivovať autentifikačnú aplikáciu" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Na ochranu vášho účtu dvojfaktorovou autentifikáciou naskenujte QR kód " "nižšie autentifikačnou aplikáciou a následne zadajte vygenerovaný overovací " "kód nižšie." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Heslo prístupového tokenu" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Môžete si toto heslo uložiť a kedykoľvek ho použiť na preinštalovanie vašej " "autentifikačnej aplikaćie." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Deaktivovať autentifikačnú aplikáciu" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "Chystáte sa deaktivovať vašu autentifikačnú aplikáciu. Ste si istý/á?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Dôverovať tomuto prehliadaču?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Ak sa rozhodnete dôverovať tomuto prehliadaču, nebudeme sa vás pri ďalšom " "prihlásení pýtať na overovací kód." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Dôverovať na %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Nedôverovať" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Pridať bezpečnostný kľúč" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Odstrániť bezpečnostný kľúč" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Naozaj chcete odstrániť tento bezpečnostný kľúč?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Využitie" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Prístupový kľúč" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Bezpečnostný kľúč" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Tento kľúč neuvádza, či ide o prístupový kľúč." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Neuvedené" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Pridaný %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Naposledy použitý %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Upraviť" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Upraviť bezpečnostný kľúč" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Uložiť" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Vytvoriť Passkey" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Chystáte sa vytvoriť prístupový kľúč pre svoj účet. Keďže neskôr môžete " "pridať ďalšie kľúče, môžete uviesť názov, pomocou ktorého kľúče od seba " "odlíšite." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Vytvoriť" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Táto funkcionalita vyžaduje JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Zlyhanie externého prihlásenia" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "Pri prihlasovaní sa cez externý účet nastala chyba." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Môžete sa prihlásiť pomocou niektorého z nasledujúcich externých účtov:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Momentálne nemáte pripojený žiaden externý účet." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Pridať externý účet" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Externý účet %(provider)s bol pripojený do vášho účtu." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Externý účet pripojený" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "Externý účet %(provider)s bol odstránený z vášho účtu." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Externý účet odstránený" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Pripojiť %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Chystáte sa pripojiť nový externý účet %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Prihlásiť sa cez %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "Chystáte sa prihlásiť cez externý účet %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Prihlásenie zrušené" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Rozhodli ste sa zrušiť prihlásenie na našu stránku pomocou externého účtu. " "Ak ste chceli vykonať inú operáciu, pokračujte na prihlásenie." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Externý účet bol pripojený." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Externý účet bol odstránený." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Chystáte sa použiť váš %(provider_name)s účet na prihlásenie do\n" "%(site_name)s. Ako posledný krok vyplňte nasledujúci formulár:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Alebo použiť externý účet" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Boli ste odhlásený/á zo všetkých ostatných relácií." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Začiatok" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP adresa" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Prehliadač" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Naposledy použitá" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Aktuálna relácia" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Odhlásiť sa z ostatných relácií" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Relácie používateľa" #: usersessions/models.py:94 msgid "session key" msgstr "klúč relácie" #~ msgid "Account Connection" #~ msgstr "Pripojené účty" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Heslo musí mať aspoň {0} znakov." #, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Tento e-mail ste dostali, pretože niekto požiadal o heslo k Vášmu\n" #~ "používateľskému účtu. V našej databáze však nemáme žiadny záznam o " #~ "používateľovi\n" #~ "s e-mailom %(email)s.\n" #~ "\n" #~ "Ak ste nežidali o obnovenie hesla, môžete tento e-mail pokojne " #~ "ignorovať.\n" #~ "\n" #~ "Ak ste to boli Vy, môžete si zaregistrovať účet pomocou odkazu nižšie." #~ msgid "The following email address is associated with your account:" #~ msgstr "Nasledujúce e-mailové adresy sú prepojené s vašim účtom:" #~ msgid "Change Email Address" #~ msgstr "Potvrdiť e-mailovú adresu" #~ msgid "" #~ "To safeguard the security of your account, please enter your password:" #~ msgstr "Kvôli bezpečnosti vášho účtu, zadajte prosím vaše heslo:" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Prosím prihláste sa s jedným\n" #~ "z vašich existujúcich účtov iných služieb. Alebo sa zaregistrujte\n" #~ "na %(site_name)s a prihláste sa nižšie:" #~ msgid "or" #~ msgstr "alebo" #~ msgid "change password" #~ msgstr "zmeniť heslo" #~ msgid "OpenID Sign In" #~ msgstr "Prihlásiť pomocou OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "Táto e-mailová adresa je už spojená s iným účtom." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Odoslali sme vám e-mail. Prosím kontaktujte nás ak ste ho nedostali do " #~ "pár minút." ================================================ FILE: allauth/locale/sl/LC_MESSAGES/django.po ================================================ # Slovenian translations for Django-allauth. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Lev Predan Kowarski , 2020-06-27 # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-07-25 20:27+0200\n" "Last-Translator: Bojan Mihelac \n" "Language-Team: Slovenian \n" "Language: sl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || " "n%100==4 ? 2 : 3;\n" "X-Generator: Weblate 5.12.1\n" "X-Translated-Using: django-rosetta 0.7.4\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Račun trenutno ni aktiven." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Primarnega e-poštnega naslova ni mogoče odstraniti." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "E-poštni naslov že pripada vašemu uporabniškemu računu." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "E-poštni naslov in/ali geslo nista pravilna." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Telefonska številka in/ali geslo nista pravilna." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Za ta e-naslov že obstaja registriran uporabnik." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Prosimo vpišite trenutno geslo." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Napačna koda." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Napačno geslo." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Napačni ali potekli ključ." #: account/adapter.py:79 msgid "Invalid login." msgstr "Napačna prijava." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Žeton za ponastavitev gesla je bil neveljaven." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Ne morete dodati več kot %d e-poštnih naslovov." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Za to telefonsko številko že obstaja registriran uporabnik." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Preveliko število neuspelih prijav. Poskusite znova kasneje." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "E-poštni naslov ni dodeljen nobenemu uporabniškemu računu." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Telefonska številka ni dodeljena nobenemu uporabniškemu računu." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Vaš primarni e-poštni naslov mora biti preverjen." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "" "Uporabniško ime je neveljavno. Prosimo uporabite drugo uporabniško ime." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Uporabniško ime in/ali geslo nista pravilna." #: account/adapter.py:98 msgid "Please select only one." msgstr "Prosimo, izberite le eno možnost." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Nova vrednost mora biti drugačna od trenutne." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Bodite potrpežljivi, pošiljate preveč zahtevkov." #: account/adapter.py:826 msgid "Use your password" msgstr "Uporabite svoje geslo" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Uporabite aplikacijo za avtentikacijo ali kodo." #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Uporabite varnostni ključ." #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "E-poštni naslov {email} je bil označen kot preverjen." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "E-poštnega naslova {email} ni bilo mogoče označiti kot preverjenega." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Označi izbrane e-poštne naslove kot preverjene." #: account/apps.py:11 msgid "Accounts" msgstr "Računi" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-poštni naslov" #: account/fields.py:19 msgid "Email address" msgstr "E-poštni naslov" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Vnesite telefonsko številko, vključno s kodo države (npr. +1 za ZDA)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefon" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Vnesti je potrebno isto geslo." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Geslo" #: account/forms.py:67 msgid "Remember Me" msgstr "Zapomni si me" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Uporabniško ime" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Prijava" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Uporabniško ime, e-poštni naslov ali telefon" #: account/forms.py:117 msgid "Username or email" msgstr "Uporabniško ime ali e-poštni naslov" #: account/forms.py:119 msgid "Username or phone" msgstr "Uporabniško ime ali telefon" #: account/forms.py:121 msgid "Email or phone" msgstr "E-poštni naslov ali telefon" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Ste pozabili geslo?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-pooštni naslov (ponovno)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Potrditev e-poštni naslova" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-poštni naslov (neobvezno)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Uporabniško ime (neobvezno)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Vnesti je potrebno isti e-poštni naslov." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Geslo (ponovno)" #: account/forms.py:591 msgid "Current Password" msgstr "Trenutno geslo" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Novo geslo" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Novo geslo (ponovno)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Koda" #: account/models.py:23 msgid "user" msgstr "uporabnik" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "E-poštni naslov" #: account/models.py:31 msgid "verified" msgstr "preverjeno" #: account/models.py:32 msgid "primary" msgstr "Primarni" #: account/models.py:38 msgid "email addresses" msgstr "E-poštni naslovi" #: account/models.py:142 msgid "created" msgstr "ustvarjeno" #: account/models.py:143 msgid "sent" msgstr "poslano" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "ključ" #: account/models.py:149 msgid "email confirmation" msgstr "E-poštna potrditev" #: account/models.py:150 msgid "email confirmations" msgstr "E-poštne potrditve" #: headless/apps.py:7 msgid "Headless" msgstr "Brez vmesnika" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Ogled vašega uporabniškega ID-ja" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Ogled vašega e-poštnega naslova" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Ogled vaših osnovnih podatkov profila" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Podeli dovoljenja" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Nadomestni znaki niso dovoljeni, razen če je omogočena možnost 'Dovoli " "nadomestne znake v URI'." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' vsebuje več kot en nadomestni znak (*). Dovoljen je samo en " "nadomestni znak na URI." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Nadomestni znaki so dovoljeni samo v delu URI z imenom gostitelja." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Avtorizacijska koda" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Koda naprave" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Poverilnice odjemalca" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Žeton za osvežitev" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Zaupno" #: idp/oidc/models.py:44 msgid "Public" msgstr "Javno" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Obsegi, ki jih odjemalec sme zahtevati. Navedite eno vrednost na vrstico, " "npr.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Če odjemalec ne navede nobenega obsega, se uporabijo ti privzeti obsegi. " "Navedite eno vrednost na vrstico, npr.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Seznam dovoljenih tipov podelitev. Navedite eno vrednost na vrstico, npr.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "Seznam dovoljenih izvorov za zahteve med domenami, en na vrstico." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Dovoli nadomestne znake (*) v preusmeritvenem URI-ju in izvorih CORS. Ko je " "omogočeno, lahko URI-ji vsebujejo eno zvezdico za ujemanje s poddomenami." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Seznam dovoljenih tipov odgovorov. Navedite eno vrednost na vrstico, npr.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "odjemalec" #: idp/oidc/models.py:116 msgid "clients" msgstr "odjemalci" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Ne morete dodati e-poštnega naslova računu, zaščitenem z dvostopenjsko " "avtentikacijo." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Ne morete deaktivirati dvostopenjske avtentikacije." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Ne morete generirati obnovitvenih kod, ne da bi imeli omogočeno " "dvostopenjsko avtentikacijo." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Dvostopenjske avtentikacije ne morete aktivirati, dokler ne potrdite svojega " "e-poštnega naslova." #: mfa/adapter.py:141 msgid "Master key" msgstr "Glavni ključ" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Varnostni ključ" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Ključ št. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Obnovitvene kode" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP avtentikator" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Koda avtentikatorja" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Brez gesla" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Omogočanje delovanja brez gesla vam omogoča prijavo z uporabo tega ključa, " "vendar nalaga dodatne zahteve, kot so biometrija ali zaščita z geslom PIN." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Obstoja uporabniški račun s tem e-poštnim naslovom. Najprej se prijavite v " "ta račun, nato pa povežite svoj %s račun." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Napačen žeton." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Vaš uporabniški račun nima nastavljenega gesla." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Vaš uporabniški račun nima preverjenega e-poštnega naslova." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "" "Ne morete odklopiti svojega zadnjega preostalega računa drugega ponudnika." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Račun drugega ponudnika je že povezan z drugim računom." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Računi družbenih omrežij." #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "ponudnik" #: socialaccount/models.py:53 msgid "provider ID" msgstr "ID ponudnika" #: socialaccount/models.py:57 msgid "name" msgstr "ime" #: socialaccount/models.py:59 msgid "client id" msgstr "id številka" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "ID aplikacije ali uporoabniški ključ" #: socialaccount/models.py:64 msgid "secret key" msgstr "skrivni ključ" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API skrivnost, skrivnost klienta ali uporabniška skrivnost" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Ključ" #: socialaccount/models.py:82 msgid "social application" msgstr "družbena aplikacija" #: socialaccount/models.py:83 msgid "social applications" msgstr "družbene aplikacije" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "zadnja prijava" #: socialaccount/models.py:121 msgid "date joined" msgstr "datum pridružitve" #: socialaccount/models.py:122 msgid "extra data" msgstr "dodatni podatki" #: socialaccount/models.py:126 msgid "social account" msgstr "uporabniški račun družbenih omerižij" #: socialaccount/models.py:127 msgid "social accounts" msgstr "uporabniški računi družbenih omerižij" #: socialaccount/models.py:161 msgid "token" msgstr "žeton" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) ali žeton za dostop (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "žeton skrivnost" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) ali žeton za osvežitev (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "veljavnost poteče" #: socialaccount/models.py:175 msgid "social application token" msgstr "žeton družebnih omrežij" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "žetoni družbenih omrežij" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Nevelljavni podatki profila" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Prijava" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Prekliči" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Neveljaven odgovor ob pridobivanju žetona za zahtevo od \"%s\". Odgovor je " "bil: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Neveljaven odgovor ob pridobivanju žetona za dostop od \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Za \"%s\" ni shranjenega žetona za zahtevo." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Za \"%s\" ni shranjenega žetona za dostop." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Ni dostopa do zasebnega vira na \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Neveljaven odgovor ob pridobivanju žetona za zahtevo od \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Neaktiven račun" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Račun ni aktiven." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Poslali smo kodo na %(recipient)s. Koda kmalu poteče, zato jo prosimo " "vnesite." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Potrdi" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Zahtevaj novo kodo" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Potrdite dostop" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Prosimo, ponovno se prijavite, da zaščitite svoj račun." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Alternativne možnosti" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Potrditev e-pošte" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Vnesite kodo za potrditev e-pošte" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Uporabite drug e-poštni naslov." #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Prijava" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Vnesite kodo za prijavo" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Ponastavitev gesla" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Vnesite kodo za ponastavitev gesla" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Potrditev telefona" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Vnesite verifikacijsko kodo za telefon" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Uporabite drugo telefonsko številko." #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "E-poštni naslovi" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "E-poštni naslovi, ki pripadajo vašemu uporabniškemu računu:" #: templates/account/email.html:25 msgid "Verified" msgstr "Preverjeni" #: templates/account/email.html:29 msgid "Unverified" msgstr "Nepreverjeni" #: templates/account/email.html:34 msgid "Primary" msgstr "Primarni" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Spremeni v primarni" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Ponovno pošlji verifikacijo" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Odstrani" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Dodaj e-poštni naslov" #: templates/account/email.html:70 msgid "Add Email" msgstr "Dodaj e-pošto" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Želite odstraniti izbran e-poštni nalsov?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Prejeli ste to e-poštno sporočilo, ker ste vi ali nekdo drug poskušali " "ustvariti\n" "račun z e-poštnim naslovom:\n" "\n" "%(email)s\n" "\n" "Vendar račun z uporabo tega e-poštnega naslova že obstaja. V kolikor ste to\n" "pozabili, uporabite postopek za pozabljeno geslo za obnovitev\n" "vašega računa:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Račun že obstaja" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Pozdravljeni iz %(site_name)s" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Hvala, ker uporabljate %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Prejeli ste to e-sporočilo, ker je bila na vašem računu izvršena naslednja " "sprememba:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Če te spremembe ne prepoznate, takoj sprejmite ustrezne varnostne ukrepe. " "Sprememba na vašem računu izhaja iz:\n" "\n" "- IP naslov: %(ip)s\n" "- Brskalnik: %(user_agent)s\n" "- Datum: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "" "Vaš e-poštni naslov je bil spremenjen iz %(from_email)s na %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "E-poštni naslov spremenjen" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Vaš e-poštni naslov je potrjen." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Potrditev e-pošte" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "To sporočilo ste prejeli, ker je uporabnik %(user_display)s navedel vaš e-" "poštni naslov za registracijo računa na %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Vaša koda za potrditev e-pošte je navedena spodaj. Prosim, vnesite jo v " "odprtem oknu brskalnika." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Da potrdite, da je to pravilno, sledite povezavi %(activate_url)s." #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Prosimo, potrdite svoj e-poštni naslov." #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "E-poštni naslov %(deleted_email)s je bil odstranjen iz vašega računa." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "E-poštni naslov odstranjen" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Vaša koda za prijavo je navedena spodaj. Prosim, vnesite jo v odprtem oknu " "brskalnika." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "To sporočilo lahko varno prezrete, če niste sprožili te akcije." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Koda za prijavo" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Vaše geslo je bilo spremenjeno." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Geslo spremenjeno" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Vaša koda za ponastavitev gesla je navedena spodaj. Prosim, vnesite jo v " "odprtem oknu brskalnika." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Koda za ponastavitev gesla" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Prejeli ste to e-sporočilo, ker ste vi ali nekdo drug zahtevali ponastavitev " "gesla za vašo uporabniško račun.\n" "To sporočilo lahko prezrete, če niste zahtevali ponastavitve gesla. Sledite " "povezavi za ponastavitev gesla." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "V primeru, da ste pozabili, vaše uporabniško ime je: %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "E-poštni naslov za ponastavitev gesla" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Vaše geslo je bilo ponastavljeno." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Vaše geslo je bilo nastavljeno." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Nastavitev gesla" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Prejeli ste to e-poštno sporočilo, ker ste vi ali nekdo drug poskušali " "dostopati do računa z e-poštanem naslovom %(email)s. Vendar pa nimamo " "nobenega zapisa takšnega računa v naši bazi podatkov." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "Če ste to vi, se lahko registrirate za račun s povezavo spodaj." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Neznan račun" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "E-poštni naslov" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Trenutni e-poštni naslov" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Sprememba na" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Vaš e-poštni naslov čaka na potrditev." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Prekliči spremembo" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Spremeni na" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Spremeni e-poštni naslov" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Potrdite e-poštni naslov" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Prosimo, potrdite, da je %(email)s e-poštni " "naslov uporabnika %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "Ni mogoče potrditi %(email)s, ker je že potrjen z drugim računom." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Povezava za potrditev e-pošte je potekla ali pa je neveljavna. Prosimo, pošljite nov zahtevek za potrditev e-pošte." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Če še nimate uporabniškega računa, ga ustvarite %(link)stukaj%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Prijavite se z varnostnim ključem" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Pošlji mi kodo za prijavo" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Odjava" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Ali ste prepričani, da se želite odjaviti?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Primarnega e-poštnega naslova (%(email)s) ni mogoče odstraniti." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Na %(email)s je bilo poslano potrditveno sporočilo." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "%(email)s je potrjen." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "E-poštni naslov %(email)s je bil odstranjen." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Uspešno ste se prijavili kot %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Odjavili ste se." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Koda za prijavo je bila poslana na %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Geslo je uspešno zamenjano." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Geslo je uspešno nastavljeno." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Verifikacijska koda je bila poslana na %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Telefonska številka %(phone)s je bila potrjena." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Primarni e-poštni naslov je nastavljen." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Sprememba gesla" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Ste pozabili geslo?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Ste pozabili geslo? Vnesite svoj e-poštni naslov spodaj, in poslali vam bomo " "e-pošto za ponastavitev." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Ponastavi geslo" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "V primeru težav pri ponastavljanju gesla, nas kontaktirajte." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Poslali smo vam e-sporočilo. Če ga niste prejeli, preverite svoj lažni mapo. " "V nasprotnem primeru nas kontaktirajte, če ga ne prejemate v nekaj minutah." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Napačni žeton" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Povezava za ponastavitev gesla je neveljavna oz. je bila že uporoabljena. " "Prosimo, zahtevajte novo povezavo za " "ponastavitev." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Geslo je spremenjeno." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Nastavi geslo" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Spremeni telefon" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Trenutni telefon" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Vaša telefonska številka je še vedno v verifikaciji." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Spremeni telefon" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Vnesite svoje geslo:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Prejeli boste posebno kodo za prijavo brez gesla." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Zahtevaj kodo" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Druge možnosti prijave" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Ustvari račun" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Ustvari račun" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Že imate uporabniški račun? %(link)sPrijavite se%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Registrirajte se z varnostnim ključem." #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Registracija z varnostnim ključem" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Druge možnosti" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Registracija ni mogoča" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Registracija trenutno ni mogoča" #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Opomba" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Prijavljeni ste kot %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Opozorilo:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Trenutno nimate nastavljenega e-poštnega naslova. Priporočamo vam, da dodate " "e-poštni naslov, da boste lahko prejemali obvestila, ponastavili geslo itd." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Potrdite e-poštni naslov." #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Na vaš e-poštni naslov smo poslali sporočilo za potrditev. Sledite povezavi " "za zaključek registracije. Če potrditvenega sporočila ne vidite v prejeti " "pošti, preverite mapo z neželeno pošto. Kontaktirajte nas, če potrditvenega " "sporočila ne prejmete v nekaj minutah." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Ta del strani zahteva, da preverimo vašo identiteto. V ta namen zahtevamo, " "da potrdite lastništvo svojega e-poštnega naslova. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Poslali smo vam e-poštno sporočilo za\n" "preverjanje. Kliknite na povezavo v tem e-sporočilu. Če v svoji glavnih " "mapah ne vidite potrditvenega e-sporočila, preverite mapo z nezaželeno " "pošto. V nasprotnem primeru\n" "kontaktirajte nas, če ga v nekaj minutah ne prejemate." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Opomba: še vedno lahko spremenite " "svoj e-poštni naslov." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Sporočila:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Meni:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Povezave računov" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Dvostopenjska avtentikacija" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Seje" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Avtoriziraj" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s želi dostopati do vašega računa %(site_name)s." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Vnesite kodo naprave" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Vnesite kodo, prikazano na vaši napravi." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Nadaljuj" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Potrdite napravo" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Prosimo, potrdite kodo, prikazano na vašem %(client_name)s, za avtorizacijo " "te naprave." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Zavrni" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Naprava avtorizirana" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Uspešno ste avtorizirali vašo napravo %(client_name)s." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Naprava zavrnjena" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Avtorizacija vaše naprave %(client_name)s je bila zavrnjena." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Napaka" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Ostani prijavljen" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Vaš račun je zaščiten z dvostopenjsko avtentikacijo. Prosimo, vnesite kodo " "avtentikatorja:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Skladišče novih obnovitvenih kod za dvostopenjsko avtentikacijo je bilo " "ustvarjeno." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Ustvarjene nove obnovitvene kode" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Aplikacija avtentikatorja je bila aktivirana." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Aplikacija avtentikatorja je aktivirana." #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Aplikacija avtentikatorja je bila deaktivirana." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Aplikacija avtentikatorja je deaktivirana." #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Dodali smo nov varnostni ključ." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Varnostni ključ dodan" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Varnostni ključ je bil odstranjen." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Varnostni ključ odstranjen" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Aplikacija avtentikator" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Avtentikacija z uporabo aplikacije avtentikatorja je aktivna." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Aplikacija avtentikator ni aktivna." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Deaktiviraj" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Aktiviraj" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Varnostni ključi" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Dodali ste %(count)s varnostni ključ." msgstr[1] "Dodali ste %(count)s varnostna ključa." msgstr[2] "Dodali ste %(count)s varnostne ključe." msgstr[3] "Dodali ste %(count)s varnostnih ključev." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Nobeni varnostni ključi niso bili dodani." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Upravljaj" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Dodaj" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Obnovitvene kode" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "Na voljo je %(unused_count)s od %(total_count)s obnovitvene kode." msgstr[1] "Na voljo sta %(unused_count)s od %(total_count)s obnovitvenih kod." msgstr[2] "Na voljo so %(unused_count)s od %(total_count)s obnovitvenih kod." msgstr[3] "Na voljo je %(unused_count)s od %(total_count)s obnovitvenih kod." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Nobene obnovitvene kode niso bile nastavljene." #: templates/mfa/index.html:96 msgid "View" msgstr "Ogled" #: templates/mfa/index.html:102 msgid "Download" msgstr "Prenesi" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Generiraj" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Ustvarjen je nov sklop obnovitvenih kod." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Varnostni ključ dodan." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Varnostni ključ odstranjen." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Vnesite kodo avtentikatorja:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Načrtujete, da ustvarite nov sklop obnovitvenih kod za svoj račun." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Ta dejanja bo neveljavna vaše obstoječe kode." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Ali ste prepričani?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Neporabljene kode" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Prenesi kode" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Ustvari nove kode" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Aktiviraj aplikacijo avtentikatorja" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Za zaščito svojega računa z dvostopenjsko avtentikacijo, skenirajte QR kodo " "spodaj z aplikacijo avtentikatorja. Nato vnesite verifikacijsko kodo, ki jo " "aplikacija ustvari spodaj." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Skrivnost avtentikatorja" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "To skrivnost lahko shranite in jo uporabite za ponovno namestitev svoje " "aplikacije avtentikatorja pozneje." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Deaktiviraj aplikacijo za preverjanje" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Deaktivirali boste preverjanje z aplikacijo za preverjanje. Ali ste " "prepričani?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Zaupate temu brskalniku?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Če se odločite zaupati temu brskalniku, ob naslednji prijavi ne boste " "vprašani za potrditveno kodo." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Zaupaj za %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Ne zaupaj" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Dodaj varnostni ključ" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Odstrani varnostni ključ" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Ali ste prepričani, da želite odstraniti ta varnostni ključ?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Uporaba" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Ključ za dostop" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Varnostni ključ" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Ta ključ ne navaja, ali gre za ključ za dostop." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Nedoločeno" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Dodano %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Nazadnje uporabljeno %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Uredi" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Uredi varnostni ključ" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Shrani" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Ustvari ključ za dostop" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Ustvarili boste ključ za dostop za vaš račun. Ker lahko pozneje dodate " "dodatne ključe, lahko uporabite opisno ime za ločevanje med njimi." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Ustvari" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Ta funkcionalnost zahteva JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Neuspešna prijava z računom tretje osebe" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "Prišlo je do napake pri poskusu prijave z vašim računom tretje osebe." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "V svoj račun se lahko prijavite z naslednjimi računi tretjih oseb:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Trenutno nimate povezanega nobenega računa tretje osebe." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Dodaj račun tretje osebe" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Račun tretje osebe od %(provider)s je bil povezan z vašim računom." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Račun tretje osebe je povezan" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "Račun tretje osebe od %(provider)s je bil odklopljen od vašega računa." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Račun tretje osebe je odklopljen" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Poveži %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Povezali boste nov račun tretje osebe od %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Prijava prek %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "Prijavili se boste z računom tretje osebe od %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Prijava je bila prekinjena" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Odločili ste se za prekinitev prijave v našo storitev s pomočjo obstoječega " "računa. Če gre za napako, se lahko prijavite tukaj." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Račun tretje osebe je bil povezan." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Račun tretje osebe je bil odklopljen." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Uporabili boste svoj obstoječi %(provider_name)s račun, za vpis v\n" "%(site_name)s. Prosimo izpolnite spodnji obrazec:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Ali uporabite tretjo osebo" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Odjavljeni iz vseh drugih sej." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Začetek" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP naslov" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Brskalnik" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Nazadnje viden ob" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Trenutna" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Odjavi druge seje" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Uporabniške seje" #: usersessions/models.py:94 msgid "session key" msgstr "ključ seje" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Povezave računov" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Geslo mora vsebovati najmanj {0} znakov. " #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Pozdravljeni od %(site_name)s!\n" #~ "To sporočilo ste prejeli, ker ste zahtevali ponastavitev gesla na " #~ "%(site_name)s!\n" #~ "To sporočilo lahko zavržete, če niste poslali zahtevka za spremembo " #~ "gesla. Za ponastavitev gelsa, sledie spodnji pvezavi:" #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "E-poštni naslovi, ki pripadajo vašemu uporabniškemu računu:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Potrdite e-poštni naslov" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Prosimo, vpišite se z enim od vaših\n" #~ "obstoječih računov, ali pa %(link)sustvarite nov račun\n" #~ "na %(site_name)s in se vpišite spodaj:" #~ msgid "or" #~ msgstr "ali" #~ msgid "change password" #~ msgstr "Sprememba gesla" #~ msgid "OpenID Sign In" #~ msgstr "Prijava z OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "E-poštni naslov že pripada drugemu uporabniškemu računu." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Povezava za ponastavitev gesla je bila poslana. Če je ne boste prejeli v " #~ "nekaj minutah, nas kontaktirajte." ================================================ FILE: allauth/locale/sr/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Nikola Vulovic , 2018. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-07-25 20:27+0200\n" "Last-Translator: Igor Jerosimić \n" "Language-Team: Serbian \n" "Language: sr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Weblate 5.13-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Овај налог је тренутно неактиван." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Не можете да уклоните примарну адресу е-поште." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Ова адреса е-поште је већ повезана са овим налогом." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Адреса е-поште и/или лозинка коју сте навели нису тачни." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Број телефона и/или лозинка које сте навели нису тачни." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Корисник је већ регистрован на овој адреси е-поште." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Молимо унесите тренутну лозинку." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Нетачан код." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Нетачна лозинка." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Неважећи или истекао кључ." #: account/adapter.py:79 msgid "Invalid login." msgstr "Неважећа пријава." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Токен за ресетовање лозинке је неважећи." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Не можете додати више од %d адреса е-поште." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Корисник је већ регистрован са овим бројем телефона." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Превише неуспелих покушаја пријављивања. Покушајте поново касније." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Адреса е-поште није додељена ниједном корисничком налогу." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Број телефона није додељен ниједном корисничком налогу." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Ваша примарна адреса е-поште мора бити потврђена." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "" "Корисничко име се не може користити. Молимо користите друго корисничко име." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Корисничко име и/или лозинка коју сте навели нису тачни." #: account/adapter.py:98 msgid "Please select only one." msgstr "Молим изаберите само један." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Нова вредност мора да се разликује од тренутне." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Будите стрпљиви, шаљете превише захтева." #: account/adapter.py:826 msgid "Use your password" msgstr "Користите своју лозинку" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Користите аутентикатор апликацију или код за аутентификацију" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Користите безбедносни кључ" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "Означите {email} као верификован." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Није успело верификовање {email}." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Означите изабране адресе е-поште као верификоване" #: account/apps.py:11 msgid "Accounts" msgstr "Налози" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "Е-пошта" #: account/fields.py:19 msgid "Email address" msgstr "Адреса е-поште" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" "Унесите број телефона укључујући позивни број земље (нпр. +381 за Србију)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Телефон" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Сваки пут морате да унесете исту лозинку." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Лозинка" #: account/forms.py:67 msgid "Remember Me" msgstr "Запамти ме" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Корисничко име" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Пријавите се" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Корисничко име, е-пошта или телефон" #: account/forms.py:117 msgid "Username or email" msgstr "Корисничко име или е-пошта" #: account/forms.py:119 msgid "Username or phone" msgstr "Корисничко име или телефон" #: account/forms.py:121 msgid "Email or phone" msgstr "Е-пошта или телефон" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Заборавили сте лозинку?" #: account/forms.py:287 msgid "Email (again)" msgstr "Е-пошта (опет)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Потврда адресе е-поште" #: account/forms.py:302 msgid "Email (optional)" msgstr "Е-пошта (опционо)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Корисничко име (опционо)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Морате унети исту адресу е-поште сваки пут." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Лозинка (поново)" #: account/forms.py:591 msgid "Current Password" msgstr "Тренутна лозинка" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Нова лозинка" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Нова лозинка (поново)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "код" #: account/models.py:23 msgid "user" msgstr "корисник" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "адреса е-поште" #: account/models.py:31 msgid "verified" msgstr "проверено" #: account/models.py:32 msgid "primary" msgstr "примарна" #: account/models.py:38 msgid "email addresses" msgstr "адресе е-поште" #: account/models.py:142 msgid "created" msgstr "створено" #: account/models.py:143 msgid "sent" msgstr "послат" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "кључ" #: account/models.py:149 msgid "email confirmation" msgstr "потврда е-поште" #: account/models.py:150 msgid "email confirmations" msgstr "потврде е-поште" #: headless/apps.py:7 msgid "Headless" msgstr "Без главе" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Погледајте свој кориснички ИД" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Погледајте своју адресу е-поште" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Погледајте основне информације о свом профилу" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Додели дозволе" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Џокер знакови нису дозвољени осим ако није омогућена опција 'Дозволи URI " "џокер знакове'." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' садржи више од једног џокер знака (*). Дозвољен је само један џокер " "знак по URI-ју." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "" "Џокер знакови су дозвољени само у делу URI-ја који се односи на име хоста." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Код ауторизације" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Код уређаја" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Акредитиви клијента" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Освежи токен" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Поверљиво" #: idp/oidc/models.py:44 msgid "Public" msgstr "Јавно" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Опсег(зи) које клијент може да захтева. Наведите једну вредност по реду, " "нпр: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "У случају да клијент не наведе опсег, користе се ови подразумевани опсези. " "Наведите по једну вредност по реду, нпр: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Листа дозвољених типова одобрења. Наведите једну вредност по реду, нпр: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Листа дозвољених порекла за захтеве из различитог порекла, по једно у сваком " "реду." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Дозволи џокер знакове (*) у преусмеравајућим URI-јима и CORS изворима. Када " "је омогућено, URI-ји могу садржати једну звездицу за подударање поддомена." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Листа дозвољених типова одговора. Наведите једну вредност по реду, нпр: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "клијент" #: idp/oidc/models.py:116 msgid "clients" msgstr "клијенти" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Не можете додати адресу е-поште на налог заштићен двофакторском " "аутентификацијом." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Не можете деактивирати двофакторску аутентификацију." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Не можете генерисати кодове за опоравак без омогућене двофакторске " "аутентификације." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Не можете активирати двофакторску аутентификацију док не верификујете своју " "адресу е-поште." #: mfa/adapter.py:141 msgid "Master key" msgstr "Главни кључ" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Резервни кључ" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Кључ бр. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Кодови за опоравак" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP аутентикатор" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Aутентикатор код" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Без лозинке" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Укључивање рада без лозинке омогућава вам да се пријавите користећи само " "овај кључ, али намеће додатне захтеве као што су биометрија или заштита ПИН-" "ом." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Већ постоји налог са овом адресом е-поште. Прво се пријавите на тај налог, а " "затим повежите свој %s налог." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Неважећи токен." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Ваш налог нема подешену лозинку." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Ваш налог нема потврђену е-маил адресу." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "" "Не можете да прекинете везу са последњим преосталим налогом треће стране." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Налог треће стране је већ повезан са другим налогом." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Друштвени налози" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "провидер" #: socialaccount/models.py:53 msgid "provider ID" msgstr "провидер ID" #: socialaccount/models.py:57 msgid "name" msgstr "име" #: socialaccount/models.py:59 msgid "client id" msgstr "ид клијента" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "ИД апликације или потрошачки кључ" #: socialaccount/models.py:64 msgid "secret key" msgstr "тајни кључ" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "Тајна АПИ-ја, тајна клијента или тајна потрошача" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Кључ" #: socialaccount/models.py:82 msgid "social application" msgstr "друштвена апликација" #: socialaccount/models.py:83 msgid "social applications" msgstr "друштвене апликације" #: socialaccount/models.py:118 msgid "uid" msgstr "уид" #: socialaccount/models.py:120 msgid "last login" msgstr "последња пријава" #: socialaccount/models.py:121 msgid "date joined" msgstr "датум придруживања" #: socialaccount/models.py:122 msgid "extra data" msgstr "додатни подаци" #: socialaccount/models.py:126 msgid "social account" msgstr "друштвени налог" #: socialaccount/models.py:127 msgid "social accounts" msgstr "друштвени налози" #: socialaccount/models.py:161 msgid "token" msgstr "токен" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) или токен приступа (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "токен тајна" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) или токен за освежавање (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "истиче у" #: socialaccount/models.py:175 msgid "social application token" msgstr "токен друштвених апликација" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "токени друштвених апликација" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Невељавни подаци о профилу" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Пријавите се" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Откажи" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Неважећи одговор при добијању токена за захтев од %s. Одговор је био: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Неважећи одговор при добијању токена за приступ од \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Нема сачуваних токена за захтев од \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Нема сачуваних токена за приступ од \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Нема приступа приватним ресурсима у \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Неважећи одговор при добијању токена за захтев од \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Налог је неактиван" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Овај налог је неактиван." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Послали смо код на %(recipient)s. Код унесите ускоро јер важи кратко време." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Потврди" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Захтевај нови код" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Потврди приступ" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Поново потврдите аутентичност да бисте заштитили свој налог." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Алтернативне опције" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Потврда е-поште" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Унесите потврдни код е-поште" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Користи другу адресу е-поште" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Пријавите се" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Унесите код за пријаву" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Ресетовање лозинке" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Унесите код за ресетовање лозинке" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Потврда телефона" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Унесите потврдни код за телефон" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Користи други број телефона" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Адресе е-поште" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "С вашим налогом су повезане следеће адресе е-пошти:" #: templates/account/email.html:25 msgid "Verified" msgstr "Потврђено" #: templates/account/email.html:29 msgid "Unverified" msgstr "Непотврђени" #: templates/account/email.html:34 msgid "Primary" msgstr "Примарни" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Постави за примарни" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Поново пошаљи потврду" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Уклони" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Додај адресу е-поште" #: templates/account/email.html:70 msgid "Add Email" msgstr "Додај е-пошту" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Да ли стварно желите да уклоните изабрану адресу е-поште?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Примили сте ову е-пошту јер сте ви или неко други покушали да се " "региструјете за\n" "налог користећи адресу е-поште:\n" "\n" "%(email)s\n" "\n" "Међутим налог који користи ту адресу е-поште већ постоји. У случају да сте\n" "заборавили на ово, молим користите процедуру за ресетовање лозинке \n" "за опоравак вашег налога:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Налог већ постоји" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Хвала од %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Хвала вам што користите %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "Добијате ову е-пошту јер је на вашем налогу извршена следећа промена:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Ако не препознајете ову промену, одмах предузмите одговарајуће мере " "предострожности. Промена вашег налога потиче од:\n" "\n" "- ИП адреса: %(ip)s\n" "- Прегледач: %(user_agent)s\n" "- Датум: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Ваша адреса е-поште је промењена из %(from_email)s у %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Адреса е-поште промењена" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Ваша адреса е-поште је потврђена." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Потврда е-поште" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Примили сте ову е-поруку јер је корисник %(user_display)s дао вашу адресу е-" "поште при регистрацији налога на %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Ваш потврдни код е-поште је наведен испод. Унесите га у отворени прозор " "претраживача." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Да бисте потврдили да је ово тачно, идите на %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Молим вас потврдите вашу адресу е-поште" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "Адреса е-поште %(deleted_email)s је уклоњена са вашег налога." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Адреса е-поште је уклоњена" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Ваш код за пријављивање је наведен испод. Унесите га у отворени прозор " "прегледача." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Ова порука се може безбедно занемарити ако нисте ви покренули ову радњу." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "регистрациони код" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Ваша лозинка је промењена." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Лозинка је промењена" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Ваш код за ресетовање лозинке је наведен испод. Унесите га у отворени прозор " "претраживача." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Код за ресетовање лозинке" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Примили сте ову е-поруку јер сте ви или неко други затражили ресетовање " "лозинке за ваш кориснички налог.\n" "Може се безбедно занемарити ако нисте захтевали ресетовање лозинке. Кликните " "на везу испод да бисте ресетовали лозинку." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "У случају да сте заборавили, ваше корисничко име је %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Е-пошта за ресетовање лозинке" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Ваша лозинка је ресетована." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Ваша лозинка је постављена." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Лозинка је постављена" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Примили сте ову е-пошту јер сте ви или неко други покушали да приступите " "налогу са адресом е-поште %(email)s. Међутим, ми немамо никакву евиденцију о " "таквом налогу у нашој бази података." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "Ако сте то били ви, можете се пријавити за налог користећи везу испод." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Непознати налог" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Адреса е-поште" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Тренутна адреса е-поште" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Промена у" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Ваша адреса е-поште још увек чека верификацију." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Откажи промену" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Промените на" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Промените адресу е-поште" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Потврда адресе е-поште" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Молим потврдите да је %(email)s адреса е-" "поште за корисника %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "Није могуће потврдити %(email)s јер га је већ потврдио други налог." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Ова веза за потврду адресе е-поште је истекла или је неважећа. Молимо пошаљите нови захтев за потврду е-поште." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Ако још увек нисте направили налог, молимо вас прво %(link)sсе " "региструјте%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Пријавите се помоћу приступног кључа" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Пошаљите ми код за регистрацију" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Одјава" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Јесте ли сигурни да желите да се одјавите?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Не можете да уклоните примарну адресу е-поште (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Потврдна порука је послата на адресу е-поште %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Потврдили сте %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Уклоњена адреса e-поште %(email)s." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Успешно сте се пријавили као %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Одјавили сте се." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Код за регистрацију је послат на %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Лозинка је успешно промењена." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Успешно сте поставили лозинку." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Код за потврду је послат на %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Потврдили сте број телефона %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Примарна адреса е-поште постављена." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Промени лозинку" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Заборавили сте лозинку?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Заборавили сте лозинку? Унесите своју адресу е-поште испод, а ми ћемо вам " "послати поруку е-поште која вам омогућава да је ресетујете." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Ресетуј моју лозинку" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Молимо вас да нас контактирате ако имате проблема са ресетовањем ваше " "лозинке." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Послали смо вам е-пошту. Ако је нисте примили, проверите своју фасциклу са " "нежељеном поштом. У супротном, контактирајте нас ако га не добијете за " "неколико минута." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Лош токен" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Веза за ресетовање лозинке је била неважећа, вероватно зато што је већ " "коришћена. Затражите ново ресетовање " "лозинке." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Ваша лозинка је сада промењена." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Постави лозинку" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Промени телефон" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Тренутни телефон" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Ваш телефон још увек чека потврду." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Промени телефон" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Унесите своју лозинку:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Добићете посебан код за пријаву без лозинке." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Захтевај код" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Друге опције за пријављивање" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Регистрација" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Региструјте се" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Већ имате налог? Онда Вас молимо да %(link)sсе пројавите%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Региструјте се користећи приступни кључ" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Регистрација приступним кључем" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Друге опције" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Регистрација затворена" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Жао нам је, али регистрација је тренутно затворена." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Напомена" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Већ сте пријављени као %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Упозорење:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Тренутно немате подешену ниједну адресу е-поште. Заиста би требало да додате " "адресу е-поште како бисте могли да примате обавештења, ресетујете лозинку " "итд." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Потврдите Вашу адресу е-поште" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Послали смо вам е-пошту ради верификације. Пратите наведену везу да бисте " "завршили процес регистрације. Ако не видите е-поруку за верификацију у " "главном пријемном сандучету, проверите фасциклу за нежељену пошту. " "Контактирајте нас ако не примите е-поруку за верификацију у року од неколико " "минута." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Овај део сајта захтева од Вас да потврдите\n" "да сте Ви заиста особа која тврдите да јесте. У ту сврху захтевамо од вас\n" "потврдите власништво над вашом адресом е-поште. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Послали смо вам поруку е-поштом за верификацију.\n" "Молимо кликните на везу унутар те е-поруке. Ако не видите е-поруку за " "верификацију у главном пријемном сандучету, проверите фасциклу за нежељену " "пошту. Иначе\n" "контактирајте нас ако не примите е-поруку у року од неколико минута." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Напомена: и даље можете да " "промените адресу ваше е-поште." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Поруке:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Мени:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Повезани налози" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Двофакторска аутентификација" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Сесије" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Овласти" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s жели да приступи вашем %(site_name)s налогу." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Унесите код уређаја" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Унесите код приказан на вашем уређају." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Настави" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Потврди уређај" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Молимо вас да потврдите код приказан на вашем %(client_name)s налогу да " "бисте овластили овај уређај." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Одбиј" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Уређај је овлашћен" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Успешно сте овластили свој %(client_name)s уређај." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Уређај одбијен" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Овлашћење за ваш %(client_name)s уређај је одбијено." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Грешка" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Остани пријављен" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Ваш налог је заштићен двофакторском аутентификацијом. Унесите код за потврду " "идентитета:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Генерисан је нови скуп кодова за опоравак двофакторске аутентификације." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Генерисани нови кодови за опоравак" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Аутентикатор апликација је активирана." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Апликација за аутентикацију је активирана" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Аутентикатор апликација је деактивирана." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Апликација за аутентикацију је деактивирана" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Додат је нови безбедносни кључ." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Сигурносни кључ је додат" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Безбедносни кључ је уклоњен." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Сигурносни кључ је уклоњен" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Аутентикатор апликација" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Потврда идентитета помоћу апликације за аутентификацију је активна." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Апликација за аутентификацију није активна." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Деактивирај" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Активирај" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Сигурносни кључеви" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Додали сте %(count)s безбедносни кључ." msgstr[1] "Додали сте %(count)s безбедносна кључа." msgstr[2] "Додали сте %(count)s безбедносних кључева." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Није додат ниједан безбедносни кључ." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Управљај" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Додај" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Кодови за опоравак" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Доступан је %(unused_count)s од укупно %(total_count)s кодова за опоравак." msgstr[1] "" "Доступна су %(unused_count)s од укупно %(total_count)s кодова за опоравак." msgstr[2] "" "Доступно је %(unused_count)s од укупно %(total_count)s кодова за опоравак." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Нису подешени кодови за опоравак." #: templates/mfa/index.html:96 msgid "View" msgstr "Погледај" #: templates/mfa/index.html:102 msgid "Download" msgstr "Преузми" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Генериши" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Генерисан је нови скуп кодова за опоравак." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Сигурносни кључ је додат." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Сигурносни кључ је уклоњен." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Унесите код за потврду идентитета:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Управо ћете генерисати нови скуп кодова за опоравак за свој налог." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Ова радња ће поништити ваше постојеће кодове." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Јесте ли сигурни?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Неискоришћени кодови" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Преузмите кодове" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Генеришите нове кодове" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Активирајте апликацију аутентикатор" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Да заштите свој налог двофакторском аутентификацијом скенирајте следећи QR " "код помоћу апликације за аутентификацију. Затим унесите верификациони код " "који је генерисала апликација у наставку." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Аутентикатор тајна" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Можете да сачувате ову тајну и да је користите да поново подесите апликације " "за аутентификацију касније." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Деактивирајте аутентикатор апликацију" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Спремате се да деактивирате аутентификацију засновану на аутентикатор " "апликацији. Јесте ли сигурни?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Верујете овом прегледачу?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Ако одлучите да верујете овом прегледачу, од вас се неће тражити " "верификациони код следећи пут када се пријавите." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Веруј %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Не веруј" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Додај сигурносни кључ" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Уклони сигурносни кључ" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Да ли сте сигурни да желите да уклоните овај безбедносни кључ?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Употреба" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Приступни кључ" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Безбедносни кључ" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Овај кључ не показује да ли је у питању приступни кључ." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Неодређено" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Додато у %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Последње коришћено %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Измени" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Измени сигурносни кључ" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Сачувај" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Направи приступни кључ" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Управо ћете креирати приступни кључ за свој налог. Пошто касније можете да " "додате додатне кључеве, можете користити описно име да бисте их разликовали." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Направи" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Ова функционалност захтева JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Неуспешна пријава треће стране" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Дошло је до грешке при покушају да се пријавите преко налога треће стране." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Можете се пријавити на свој налог помоћу било које од следећих налога трећих " "страна:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Тренутно немате ниједан налог треће стране повезан са овим налогом." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Додајте налог треће стране" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Налог треће стране од %(provider)s је повезан са вашим налогом." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Налог треће стране је повезан" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "Налог треће стране од %(provider)s више није повезан са вашим налогом." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Налог треће стране више није повезан" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Повежите %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Управо ћете да повежете нови налог треће стране од %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Пријавите се преко %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "Управо ћете се пријавити користећи налог треће стране од %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Пријава је отказана" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Одлучили сте да откажете пријављивање на нашу веб страницу помоћу једног од " "ваших постојећих налога. Ако је ово грешка, молимо вас да пређете на и пријавите се." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Налог треће стране је повезан." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Налог треће стране више није повезан." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Управо користите свој налог код %(provider_name)s да бисте се пријавили на\n" "%(site_name)s. Као последњи корак, молимо попуните следећи образац:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Или користите трећу страну" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Одјављен са свих осталих сесија." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Започето у" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "ИП адреса" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Претраживач" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Последње виђен у" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Тренутно" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Одјавит се из других сесија" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Корисничке сесије" #: usersessions/models.py:94 msgid "session key" msgstr "кључ сесије" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Повезани рачуни" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Лозинка мора бити најмање {0} знакова." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Здраво од %(site_name)s!\n" #~ "\n" #~ "Примате ову е-маил поруку јер сте ви или неко други тражилилозинку за ваш " #~ "кориснички налог.\n" #~ "Ова порука се може игнорисати ако нисте затражили ресет лозинке. Кликните " #~ "на линк испод да бисте поништили своју лозинку." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "С вашим налогом су повезане следеће адресе е-пошти:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Потврда адресе е-поште" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Молимо Вас да се пријавите са једним од\n" #~ "постојећих рачуна трећих страна. Или, се региструјте\n" #~ "за рачун код %(site_name)s и пријавите се доле:" #~ msgid "or" #~ msgstr "или" #~ msgid "change password" #~ msgstr "промени лозинку" #~ msgid "OpenID Sign In" #~ msgstr "ОпенИД Пријава" #~ msgid "This email address is already associated with another account." #~ msgstr "Ова адреса е-поште је већ повезана са другим налогом." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Послали смо вам е-пошту. Молимо Вас да нас контактирате ако га не " #~ "примитеза неколико минута." ================================================ FILE: allauth/locale/sr_Latn/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Nikola Vulovic , 2018. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-07-25 20:27+0200\n" "Last-Translator: Igor Jerosimić \n" "Language-Team: Serbian (Latin script) \n" "Language: sr_Latn\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 5.13-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Ovaj nalog je trenutno neaktivan." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Ne možete da uklonite primarnu adresu e-pošte." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Ova adresa e-pošte je već povezana sa ovim nalogom." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Adresa e-pošte i/ili lozinka koju ste naveli nisu tačni." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Broj telefona i/ili lozinka koje ste naveli nisu tačni." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Korisnik je već registrovan na ovoj adresi e-pošte." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Molimo unesite trenutnu lozinku." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Netačan kod." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Netačna lozinka." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Nevažeći ili istekao ključ." #: account/adapter.py:79 msgid "Invalid login." msgstr "Nevažeća prijava." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Token za resetovanje lozinke je nevažeći." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Ne možete dodati više od %d adresa e-pošte." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Korisnik je već registrovan sa ovim brojem telefona." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Previše neuspelih pokušaja prijavljivanja. Pokušajte ponovo kasnije." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Adresa e-pošte nije dodeljena nijednom korisničkom nalogu." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Broj telefona nije dodeljen nijednom korisničkom nalogu." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Vaša primarna adresa e-pošte mora biti potvrđena." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "" "Korisničko ime se ne može koristiti. Molimo koristite drugo korisničko ime." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Korisničko ime i/ili lozinka koju ste naveli nisu tačni." #: account/adapter.py:98 msgid "Please select only one." msgstr "Molim izaberite samo jedan." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Nova vrednost mora da se razlikuje od trenutne." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Budite strpljivi, šaljete previše zahteva." #: account/adapter.py:826 msgid "Use your password" msgstr "Koristite svoju lozinku" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Koristite autentikator aplikaciju ili kod za autentifikaciju" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Koristite bezbednosni ključ" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "Označite {email} kao verifikovan." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Nije uspelo verifikovanje {email}." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Označite izabrane adrese e-pošte kao verifikovane" #: account/apps.py:11 msgid "Accounts" msgstr "Nalozi" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-pošta" #: account/fields.py:19 msgid "Email address" msgstr "Adresa e-pošte" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" "Unesite broj telefona uključujući pozivni broj zemlje (npr. +381 za Srbiju)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefon" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Svaki put morate da unesete istu lozinku." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Lozinka" #: account/forms.py:67 msgid "Remember Me" msgstr "Zapamti me" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Korisničko ime" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Prijavite se" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Korisničko ime, e-pošta ili telefon" #: account/forms.py:117 msgid "Username or email" msgstr "Korisničko ime ili e-pošta" #: account/forms.py:119 msgid "Username or phone" msgstr "Korisničko ime ili telefon" #: account/forms.py:121 msgid "Email or phone" msgstr "E-pošta ili telefon" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Zaboravili ste lozinku?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-pošta (opet)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Potvrda adrese e-pošte" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-pošta (opciono)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Korisničko ime (opciono)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Morate uneti istu adresu e-pošte svaki put." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Lozinka (ponovo)" #: account/forms.py:591 msgid "Current Password" msgstr "Trenutna lozinka" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Nova lozinka" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Nova lozinka (ponovo)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "kod" #: account/models.py:23 msgid "user" msgstr "korisnik" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "adresa e-pošte" #: account/models.py:31 msgid "verified" msgstr "provereno" #: account/models.py:32 msgid "primary" msgstr "primarna" #: account/models.py:38 msgid "email addresses" msgstr "adrese e-pošte" #: account/models.py:142 msgid "created" msgstr "stvoreno" #: account/models.py:143 msgid "sent" msgstr "poslat" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "ključ" #: account/models.py:149 msgid "email confirmation" msgstr "potvrda e-pošte" #: account/models.py:150 msgid "email confirmations" msgstr "potvrde e-pošte" #: headless/apps.py:7 msgid "Headless" msgstr "Bez glave" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Pogledajte svoj korisnički ID" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Pogledajte svoju adresu e-pošte" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Pogledajte osnovne informacije o svom profilu" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Dodeli dozvole" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Džoker znakovi nisu dozvoljeni osim ako nije omogućena opcija 'Dozvoli URI " "džoker znakove'." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' sadrži više od jednog džoker znaka (*). Dozvoljen je samo jedan " "džoker znak po URI-ju." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "" "Džoker znakovi su dozvoljeni samo u delu URI-ja koji se odnosi na ime hosta." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Kod autorizacije" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Kod uređaja" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Akreditivi klijenta" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Osveži token" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Poverljivo" #: idp/oidc/models.py:44 msgid "Public" msgstr "Javno" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Opseg(zi) koje klijent može da zahteva. Navedite jednu vrednost po redu, " "npr: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "U slučaju da klijent ne navede opseg, koriste se ovi podrazumevani opsezi. " "Navedite po jednu vrednost po redu, npr: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Lista dozvoljenih tipova odobrenja. Navedite jednu vrednost po redu, npr: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Lista dozvoljenih porekla za zahteve iz različitog porekla, po jedno u " "svakom redu." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Dozvoli džoker znakove (*) u preusmeravajućim URI-jima i CORS izvorima. Kada " "je omogućeno, URI-ji mogu sadržati jednu zvezdicu za podudaranje poddomena." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Lista dozvoljenih tipova odgovora. Navedite jednu vrednost po redu, npr: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "klijent" #: idp/oidc/models.py:116 msgid "clients" msgstr "klijenti" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Ne možete dodati adresu e-pošte na nalog zaštićen dvofaktorskom " "autentifikacijom." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Ne možete deaktivirati dvofaktorsku autentifikaciju." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Ne možete generisati kodove za oporavak bez omogućene dvofaktorske " "autentifikacije." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Ne možete aktivirati dvofaktorsku autentifikaciju dok ne verifikujete svoju " "adresu e-pošte." #: mfa/adapter.py:141 msgid "Master key" msgstr "Glavni ključ" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Rezervni ključ" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Ključ br. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Kodovi za oporavak" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP autentikator" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Autentikator kod" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Bez lozinke" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Uključivanje rada bez lozinke omogućava vam da se prijavite koristeći samo " "ovaj ključ, ali nameće dodatne zahteve kao što su biometrija ili zaštita PIN-" "om." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Već postoji nalog sa ovom adresom e-pošte. Prvo se prijavite na taj nalog, a " "zatim povežite svoj %s nalog." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Nevažeći token." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Vaš nalog nema podešenu lozinku." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Vaš nalog nema potvrđenu e-mail adresu." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "" "Ne možete da prekinete vezu sa poslednjim preostalim nalogom treće strane." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Nalog treće strane je već povezan sa drugim nalogom." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Društveni nalozi" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "provider" #: socialaccount/models.py:53 msgid "provider ID" msgstr "provider ID" #: socialaccount/models.py:57 msgid "name" msgstr "ime" #: socialaccount/models.py:59 msgid "client id" msgstr "id klijenta" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "ID aplikacije ili potrošački ključ" #: socialaccount/models.py:64 msgid "secret key" msgstr "tajni ključ" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "Tajna API-ja, tajna klijenta ili tajna potrošača" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Ključ" #: socialaccount/models.py:82 msgid "social application" msgstr "društvena aplikacija" #: socialaccount/models.py:83 msgid "social applications" msgstr "društvena aplikacije" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "poslednja prijava" #: socialaccount/models.py:121 msgid "date joined" msgstr "datum pridruživanja" #: socialaccount/models.py:122 msgid "extra data" msgstr "dodatni podaci" #: socialaccount/models.py:126 msgid "social account" msgstr "društveni nalog" #: socialaccount/models.py:127 msgid "social accounts" msgstr "društveni nalozi" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) ili token pristupa (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "token tajna" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) ili token za osvežavanje (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "ističe u" #: socialaccount/models.py:175 msgid "social application token" msgstr "token društvenih aplikacija" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "tokeni društvenih aplikacija" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Neveljavni podaci o profilu" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Prijavite se" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Otkaži" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Nevažeći odgovor pri dobijanju tokena za zahtev od %s. Odgovor je bio: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Nevažeći odgovor pri dobijanju tokena za pristup od \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Nema sačuvanih tokena za zahtev od \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Nema sačuvanih tokena za pristup od \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Nema pristupa privatnim resursima u \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Nevažeći odgovor pri dobijanju tokena za zahtev od \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Nalog je neaktivan" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Ovaj nalog je neaktivan." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Poslali smo kod na %(recipient)s. Kod unesite uskoro jer važi kratko vreme." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Potvrdi" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Zahtevaj novi kod" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Potvrdi pristup" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Ponovo potvrdite autentičnost da biste zaštitili svoj nalog." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Alternativne opcije" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Potvrda e-pošte" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Unesite potvrdni kod e-pošte" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Koristi drugu adresu e-pošte" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Prijavite se" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Unesite kod za prijavu" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Resetovanje lozinke" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Unesite kod za resetovanje lozinke" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Potvrda telefona" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Unesite potvrdni kod za telefon" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Koristi drugi broj telefona" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Adrese e-pošte" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "S vašim nalogom su povezane sledeće adrese e-pošti:" #: templates/account/email.html:25 msgid "Verified" msgstr "Potvrđeno" #: templates/account/email.html:29 msgid "Unverified" msgstr "Nepotvrđeni" #: templates/account/email.html:34 msgid "Primary" msgstr "Primarni" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Postavi za primarni" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Ponovo pošalji potvrdu" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Ukloni" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Dodaj adresu e-pošte" #: templates/account/email.html:70 msgid "Add Email" msgstr "Dodaj e-poštu" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Da li stvarno želite da uklonite izabranu adresu e-pošte?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Primili ste ovu e-poštu jer ste vi ili neko drugi pokušali da se " "registrujete za\n" "nalog koristeći adresu e-pošte:\n" "\n" "%(email)s\n" "\n" "Međutim nalog koji koristi tu adresu e-pošte već postoji. U slučaju da ste\n" "zaboravili na ovo, molim koristite proceduru za resetovanje lozinke \n" "za oporavak vašeg naloga:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Nalog već postoji" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Hvala od %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Hvala vam što koristite %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "Dobijate ovu e-poštu jer je na vašem nalogu izvršena sledeća promena:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Ako ne prepoznajete ovu promenu, odmah preduzmite odgovarajuće mere " "predostrožnosti. Promena vašeg naloga potiče od:\n" "\n" "- IP adresa: %(ip)s\n" "- Pregledač: %(user_agent)s\n" "- Datum: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Vaša adresa e-pošte je promenjena iz %(from_email)s u %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Adresa e-pošte promenjena" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Vaša adresa e-pošte je potvrđena." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Potvrda e-pošte" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Primili ste ovu e-poruku jer je korisnik %(user_display)s dao vašu adresu e-" "pošte pri registraciji naloga na %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Vaš potvrdni kod e-pošte je naveden ispod. Unesite ga u otvoreni prozor " "pretraživača." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Da biste potvrdili da je ovo tačno, idite na %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Molim vas potvrdite vašu adresu e-pošte" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "Adresa e-pošte %(deleted_email)s je uklonjena sa vašeg naloga." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Adresa e-pošte je uklonjena" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Vaš kod za prijavljivanje je naveden ispod. Unesite ga u otvoreni prozor " "pregledača." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Ova poruka se može bezbedno zanemariti ako niste vi pokrenuli ovu radnju." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "registracioni kod" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Vaša lozinka je promenjena." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Lozinka je promenjena" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Vaš kod za resetovanje lozinke je naveden ispod. Unesite ga u otvoreni " "prozor pretraživača." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Kod za resetovanje lozinke" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Primili ste ovu e-poruku jer ste vi ili neko drugi zatražili resetovanje " "lozinke za vaš korisnički nalog.\n" "Može se bezbedno zanemariti ako niste zahtevali resetovanje lozinke. " "Kliknite na vezu ispod da biste resetovali lozinku." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "U slučaju da ste zaboravili, vaše korisničko ime je %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "E-pošta za resetovanje lozinke" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Vaša lozinka je resetovana." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Vaša lozinka je postavljena." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Lozinka je postavljena" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Primili ste ovu e-poštu jer ste vi ili neko drugi pokušali da pristupite " "nalogu sa adresom e-pošte %(email)s. Međutim, mi nemamo nikakvu evidenciju o " "takvom nalogu u našoj bazi podataka." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "Ako ste to bili vi, možete se prijaviti za nalog koristeći vezu ispod." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Nepoznati nalog" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Adresa e-pošte" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Trenutna adresa e-pošte" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Promena u" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Vaša adresa e-pošte još uvek čeka verifikaciju." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Otkaži promenu" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Promenite na" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Promenite adresu e-pošte" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Potvrda adrese e-pošte" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Molim potvrdite da je %(email)s adresa e-" "pošte za korisnika %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "Nije moguće potvrditi %(email)s jer ga je već potvrdio drugi nalog." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Ova veza za potvrdu adrese e-pošte je istekla ili je nevažeća. Molimo pošaljite novi zahtev za potvrdu e-pošte." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Ako još uvek niste napravili nalog, molimo vas prvo %(link)sse " "registrujte%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Prijavite se pomoću pristupnog ključa" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Pošaljite mi kod za registraciju" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Odjava" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Jeste li sigurni da želite da se odjavite?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Ne možete da uklonite primarnu adresu e-pošte (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Potvrdna poruka je poslata na adresu e-pošte %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Potvrdili ste %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Uklonjena adresa e-pošte %(email)s." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Uspešno ste se prijavili kao %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Odjavili ste se." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Kod za registraciju je poslat na %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Lozinka je uspešno promenjena." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Uspešno ste postavili lozinku." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Kod za potvrdu je poslat na %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Potvrdili ste broj telefona %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Primarna adresa e-pošte postavljena." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Promeni lozinku" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Zaboravili ste lozinku?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Zaboravili ste lozinku? Unesite svoju adresu e-pošte ispod, a mi ćemo vam " "poslati poruku e-pošte koja vam omogućava da je resetujete." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Resetuj moju lozinku" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Molimo vas da nas kontaktirate ako imate problema sa resetovanjem vaše " "lozinke." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Poslali smo vam e-poštu. Ako je niste primili, proverite svoju fasciklu sa " "neželjenom poštom. U suprotnom, kontaktirajte nas ako ga ne dobijete za " "nekoliko minuta." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Loš token" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Veza za resetovanje lozinke je bila nevažeća, verovatno zato što je već " "korišćena. Zatražite novo resetovanje " "lozinke." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Vaša lozinka je sada promenjena." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Postavi lozinku" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Promeni telefon" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Trenutni telefon" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Vaš telefon još uvek čeka potvrdu." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Promeni telefon" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Unesite svoju lozinku:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Dobićete poseban kod za prijavu bez lozinke." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Zahtevaj kod" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Druge opcije za prijavljivanje" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Registracija" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Registrujte se" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Već imate nalog? Onda Vas molimo da %(link)sse projavite%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Registrujte se koristeći pristupni ključ" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Registracija pristupnim ključem" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Druge opcije" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Registracija zatvorena" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Žao nam je, ali registracija je trenutno zatvorena." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Napomena" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Već ste prijavljeni kao %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Upozorenje:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Trenutno nemate podešenu nijednu adresu e-pošte. Zaista bi trebalo da dodate " "adresu e-pošte kako biste mogli da primate obaveštenja, resetujete lozinku " "itd." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Potvrdite Vašu adresu e-pošte" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Poslali smo vam e-poštu radi verifikacije. Pratite navedenu vezu da biste " "završili proces registracije. Ako ne vidite e-poruku za verifikaciju u " "glavnom prijemnom sandučetu, proverite fasciklu za neželjenu poštu. " "Kontaktirajte nas ako ne primite e-poruku za verifikaciju u roku od nekoliko " "minuta." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Ovaj deo sajta zahteva od Vas da potvrdite\n" "da ste Vi zaista osoba koja tvrdite da jeste. U tu svrhu zahtevamo od vas\n" "potvrdite vlasništvo nad vašom adresom e-pošte. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Poslali smo vam poruku e-poštom za verifikaciju.\n" "Molimo kliknite na vezu unutar te e-poruke. Ako ne vidite e-poruku za " "verifikaciju u glavnom prijemnom sandučetu, proverite fasciklu za neželjenu " "poštu. Inače\n" "kontaktirajte nas ako ne primite e-poruku u roku od nekoliko minuta." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Napomena: i dalje možete da " "promenite adresu vaše e-pošte." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Poruke:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Meni:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Povezani nalozi" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Dvofaktorska autentifikacija" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sesije" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Ovlasti" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s želi da pristupi vašem %(site_name)s nalogu." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Unesite kod uređaja" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Unesite kod prikazan na vašem uređaju." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Nastavi" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Potvrdi uređaj" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Molimo vas da potvrdite kod prikazan na vašem %(client_name)s nalogu da " "biste ovlastili ovaj uređaj." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Odbij" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Uređaj je ovlašćen" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Uspešno ste ovlastili svoj %(client_name)s uređaj." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Uređaj odbijen" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Ovlašćenje za vaš %(client_name)s uređaj je odbijeno." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Greška" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Ostani prijavljen" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Vaš nalog je zaštićen dvofaktorskom autentifikacijom. Unesite kod za potvrdu " "identiteta:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Generisan je novi skup kodova za oporavak dvofaktorske autentifikacije." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Generisani novi kodovi za oporavak" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Autentikator aplikacija je aktivirana." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Aplikacija za autentikaciju je aktivirana" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Autentikator aplikacija je deaktivirana." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Aplikacija za autentikaciju je deaktivirana" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Dodat je novi bezbednosni ključ." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Sigurnosni ključ je dodat" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Bezbednosni ključ je uklonjen." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Sigurnosni ključ je uklonjen" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Autentikator aplikacija" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Potvrda identiteta pomoću aplikacije za autentifikaciju je aktivna." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Aplikacija za autentifikaciju nije aktivna." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Deaktiviraj" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Aktiviraj" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Sigurnosni ključevi" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Dodali ste %(count)s bezbednosni ključ." msgstr[1] "Dodali ste %(count)s bezbednosna ključa." msgstr[2] "Dodali ste %(count)s bezbednosnih ključeva." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Nije dodat nijedan bezbednosni ključ." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Upravljaj" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Dodaj" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Kodovi za oporavak" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Dostupan je %(unused_count)s od ukupno %(total_count)s kodova za oporavak." msgstr[1] "" "Dostupna su %(unused_count)s od ukupno %(total_count)s kodova za oporavak." msgstr[2] "" "Dostupno je %(unused_count)s od ukupno %(total_count)s kodova za oporavak." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Nisu podešeni kodovi za oporavak." #: templates/mfa/index.html:96 msgid "View" msgstr "Pogledaj" #: templates/mfa/index.html:102 msgid "Download" msgstr "Preuzmi" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Generiši" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Generisan je novi skup kodova za oporavak." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Sigurnosni ključ je dodat." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Sigurnosni ključ je uklonjen." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Unesite kod za potvrdu identiteta:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Upravo ćete generisati novi skup kodova za oporavak za svoj nalog." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Ova radnja će poništiti vaše postojeće kodove." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Jeste li sigurni?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Neiskorišćeni kodovi" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Preuzmite kodove" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Generišite nove kodove" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Aktivirajte aplikaciju autentikator" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Da zaštite svoj nalog dvofaktorskom autentifikacijom skenirajte sledeći QR " "kod pomoću aplikacije za autentifikaciju. Zatim unesite verifikacioni kod " "koji je generisala aplikacija u nastavku." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Autentikator tajna" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Možete da sačuvate ovu tajnu i da je koristite da ponovo podesite aplikacije " "za autentifikaciju kasnije." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Deaktivirajte autentikator aplikaciju" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Spremate se da deaktivirate autentifikaciju zasnovanu na autentikator " "aplikaciji. Jeste li sigurni?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Verujete ovom pregledaču?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Ako odlučite da verujete ovom pregledaču, od vas se neće tražiti " "verifikacioni kod sledeći put kada se prijavite." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Veruj %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Ne veruj" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Dodaj sigurnosni ključ" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Ukloni sigurnosni ključ" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Da li ste sigurni da želite da uklonite ovaj bezbednosni ključ?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Upotreba" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Pristupni ključ" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Bezbednosni ključ" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Ovaj ključ ne pokazuje da li je u pitanju pristupni ključ." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Neodređeno" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Dodato u %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Poslednje korišćeno %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Izmeni" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Izmeni sigurnosni ključ" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Sačuvaj" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Napravi pristupni ključ" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Upravo ćete kreirati pristupni ključ za svoj nalog. Pošto kasnije možete da " "dodate dodatne ključeve, možete koristiti opisno ime da biste ih razlikovali." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Napravi" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Ova funkcionalnost zahteva JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Neuspešna prijava treće strane" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Došlo je do greške pri pokušaju da se prijavite preko naloga treće strane." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Možete se prijaviti na svoj nalog pomoću bilo koje od sledećih naloga trećih " "strana:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Trenutno nemate nijedan nalog treće strane povezan sa ovim nalogom." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Dodajte nalog treće strane" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Nalog treće strane od %(provider)s je povezan sa vašim nalogom." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Nalog treće strane je povezan" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "Nalog treće strane od %(provider)s više nije povezan sa vašim nalogom." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Nalog treće strane više nije povezan" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Povežite %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Upravo ćete da povežete novi nalog treće strane od %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Prijavite se preko %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "Upravo ćete se prijaviti koristeći nalog treće strane od %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Prijava je otkazana" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Odlučili ste da otkažete prijavljivanje na našu veb stranicu pomoću jednog " "od vaših postojećih naloga. Ako je ovo greška, molimo vas da pređete na i prijavite se." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Nalog treće strane je povezan." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Nalog treće strane više nije povezan." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Upravo koristite svoj nalog kod %(provider_name)s da biste se prijavili na\n" "%(site_name)s. Kao poslednji korak, molimo popunite sledeći obrazac:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Ili koristite treću stranu" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Odjavljen sa svih ostalih sesija." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Započeto u" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP adresa" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Pretraživač" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Poslednje viđen u" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Trenutno" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Odjavit se iz drugih sesija" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Korisničke sesije" #: usersessions/models.py:94 msgid "session key" msgstr "ključ sesije" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Povezani računi" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Lozinka mora biti najmanje {0} znakova." #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Zdravo od %(site_name)s!\n" #~ "\n" #~ "Primate ovu e-mail poruku jer ste vi ili neko drugi tražililozinku za vaš " #~ "korisnički nalog.\n" #~ "Ova poruka se može ignorisati ako niste zatražili reset lozinke. Kliknite " #~ "na link ispod da biste poništili svoju lozinku." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "S vašim nalogom su povezane sledeće adrese e-pošti:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Potvrda adrese e-pošte" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Molimo Vas da se prijavite sa jednim od\n" #~ "postojećih računa trećih strana. Ili, se registrujte\n" #~ "za račun kod %(site_name)s i prijavite se dole:" #~ msgid "or" #~ msgstr "ili" #~ msgid "change password" #~ msgstr "promeni lozinku" #~ msgid "OpenID Sign In" #~ msgstr "OpenID Prijava" #~ msgid "This email address is already associated with another account." #~ msgstr "Ova adresa e-pošte je već povezana sa drugim nalogom." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Poslali smo vam e-poštu. Molimo Vas da nas kontaktirate ako ga ne " #~ "primiteza nekoliko minuta." ================================================ FILE: allauth/locale/sv/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: msgid "" msgstr "" "Project-Id-Version: django-allauth\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-03-29 15:01+0000\n" "Last-Translator: Karl Stenlund \n" "Language-Team: Swedish \n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.11-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Detta konto är inaktivt." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Du kan inte ta bort din primära epost-adress." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Denna epost-adress är redan knuten till detta konto" #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Epost-adressen och/eller lösenordet är felaktigt." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Telefonnumret och/eller lösenordet är felaktigt." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "En användare är redan registrerad med den här epost-adressen" #: account/adapter.py:75 msgid "Please type your current password." msgstr "Skriv in ditt nuvarande lösenord." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Felaktig kod." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Felaktigt lösenord." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Felaktig eller utgången nyckel." #: account/adapter.py:79 msgid "Invalid login." msgstr "Felaktig inlogging." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Token för lösenordsåterställning var ogiltig." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Du kan inte lägga till fler än %d epost-adresser." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "En användare är redan registrerad med det här telefonnumret." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "För många misslyckade inloggningsförsök. Försök igen senare." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Epost-adressen tillhör inte något användarkonto." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Telefonnumret tillhör inte något användarkonto." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Din primära epost-adress måste verifieras." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Användarnamnet kan ej användas. Välj ett annat användarnamn." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Användarnamnet och/eller lösenordet är felaktigt." #: account/adapter.py:98 msgid "Please select only one." msgstr "Vänligen välj bara en." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Det nya värdet måste skilja sig från det nuvarande." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Ha tålamod, du skickar för många förfrågningar." #: account/adapter.py:826 msgid "Use your password" msgstr "Använd ditt lösenord" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Använd autentiseringsapp eller kod" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Använd en säkerhetsnyckel" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "Markerade {email} som verifierad." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Kunde inte markera {email} som verifierad." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Markera valda epost-adresser som verifierade" #: account/apps.py:11 msgid "Accounts" msgstr "Konton" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "Epost" #: account/fields.py:19 msgid "Email address" msgstr "Epost-adress" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Ange ett telefonnummer inklusive landskod (t.ex. +46 för Sverige)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefon" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Du måste ange samma lösenord" #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Lösenord" #: account/forms.py:67 msgid "Remember Me" msgstr "Kom ihåg mig" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Användarnamn" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Logga in" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Användarnamn, epost eller telefon" #: account/forms.py:117 msgid "Username or email" msgstr "Användarnamn eller epost-adress" #: account/forms.py:119 msgid "Username or phone" msgstr "Användarnamn eller telefon" #: account/forms.py:121 msgid "Email or phone" msgstr "Epost eller telefon" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Glömt lösenordet?" #: account/forms.py:287 msgid "Email (again)" msgstr "Epost (igen)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Bekräftelse av epost-adress" #: account/forms.py:302 msgid "Email (optional)" msgstr "Epost (valfritt)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Användarnamn (valfritt)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Du måste ange samma epost-adress varje gång." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Lösenord (igen)" #: account/forms.py:591 msgid "Current Password" msgstr "Nuvarande lösenord" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Nytt lösenord" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Nytt lösenord (igen)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Kod" #: account/models.py:23 msgid "user" msgstr "användare" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "epost-adress" #: account/models.py:31 msgid "verified" msgstr "verifierad" #: account/models.py:32 msgid "primary" msgstr "primär" #: account/models.py:38 msgid "email addresses" msgstr "epost-adresser" #: account/models.py:142 msgid "created" msgstr "skapad" #: account/models.py:143 msgid "sent" msgstr "skickad" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "nyckel" #: account/models.py:149 msgid "email confirmation" msgstr "epost-bekräftelse" #: account/models.py:150 msgid "email confirmations" msgstr "epost-bekräftelser" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Visa ditt användar-ID" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Visa din epost-adress" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Visa din grundläggande profilinformation" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Bevilja behörigheter" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Jokertecken är inte tillåtna om inte 'Tillåt URI-jokertecken' är aktiverat." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' innehåller mer än ett jokertecken (*). Endast ett jokertecken per " "URI är tillåtet." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Jokertecken är bara tillåtna i värdnamnsdelen av URI:n." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Auktoriseringskod" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Enhetskod" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Klientuppgifter" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Uppdateringstoken" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Konfidentiell" #: idp/oidc/models.py:44 msgid "Public" msgstr "Publik" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "De omfång som klienten tillåts begära. Ange ett värde per rad, t.ex.: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Om klienten inte anger något omfång används dessa standardomfång. Ange ett " "värde per rad, t.ex.: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "En lista över tillåtna beviljandetyper. Ange ett värde per rad, t.ex.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "En lista över tillåtna ursprung för cross-origin-förfrågningar, ett per rad." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Tillåt jokertecken (*) i omdirigerings-URI:er och CORS-ursprung. När " "aktiverat kan URI:er innehålla en enskild asterisk för att matcha " "underdomäner." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "En lista över tillåtna svarstyper. Ange ett värde per rad, t.ex.: " "code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "klient" #: idp/oidc/models.py:116 msgid "clients" msgstr "klienter" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Du kan inte lägga till en epost-adress till ett konto skyddat av " "tvåfaktorsautentisering." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Du kan inte inaktivera tvåfaktorsautentisering." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Du kan inte generera återställningskoder utan att ha tvåfaktorsautentisering " "aktiverad." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Du kan inte aktivera tvåfaktorsautentisering förrän du har verifierat din " "epost-adress." #: mfa/adapter.py:141 msgid "Master key" msgstr "Huvudnyckel" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Reservnyckel" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Nyckel nr. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Återställningskoder" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP-autentiserare" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Autentiseringskod" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Utan lösenord" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Att aktivera lösenordsfri drift gör att du kan logga in med bara denna " "nyckel, men kräver ytterligare säkerhet som biometri eller PIN-skydd." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Det finns redan ett konto med denna epost-adress. Vänligen logga in på det " "kontot först och anslut sedan ditt %s-konto." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Ogiltig token." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Ditt konto har inget lösenord." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Ditt konto har ingen verifierad epost-adress." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Du kan inte koppla bort ditt sista återstående tredjeparts-konto." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Tredjeparts-kontot är redan knutet till ett annat konto." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Sociala konton" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "leverantör" #: socialaccount/models.py:53 msgid "provider ID" msgstr "leverantörs-ID" #: socialaccount/models.py:57 msgid "name" msgstr "namn" #: socialaccount/models.py:59 msgid "client id" msgstr "klient-ID" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "App-ID eller konsumentnyckel" #: socialaccount/models.py:64 msgid "secret key" msgstr "hemlig nyckel" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API-hemlighet, klienthemlighet eller konsumenthemlighet" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Nyckel" #: socialaccount/models.py:82 msgid "social application" msgstr "social applikation" #: socialaccount/models.py:83 msgid "social applications" msgstr "sociala applikationer" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "senaste inloggning" #: socialaccount/models.py:121 msgid "date joined" msgstr "registreringsdatum" #: socialaccount/models.py:122 msgid "extra data" msgstr "extra data" #: socialaccount/models.py:126 msgid "social account" msgstr "socialt konto" #: socialaccount/models.py:127 msgid "social accounts" msgstr "sociala konton" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) eller åtkomsttoken (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "tokenhemlighet" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) eller uppdateringstoken (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "upphör" #: socialaccount/models.py:175 msgid "social application token" msgstr "social applikationstoken" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "sociala applikationstokens" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Ogiltig profildata" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Logga in" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Avbryt" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Felaktigt svar vid hämtning av fråge-nyckel från \"%s\". Svaret var: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Felaktigt svar vid hämtning av access-nyckel från \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Ingen fråge-nyckel sparad för \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Ingen access-nyckel sparad för \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Ingen access till privata resurser hos \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Felaktigt svar vid hämtning av fråge-nyckel från \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Kontot inaktivt" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Detta konto är inaktivt." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Vi har skickat en kod till %(recipient)s. Koden upphör snart, så ange den " "snarast." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Verifiera" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Begär ny kod" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Bekräfta åtkomst" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Vänligen autentisera dig igen för att skydda ditt konto." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Alternativa val" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Epost-verifiering" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Ange epost-verifieringskod" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Använd en annan epost-adress" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Logga in" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Ange inloggningskod" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Återställning av lösenord" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Ange kod för lösenordsåterställning" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Telefonverifiering" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Ange telefonverifieringskod" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Använd ett annat telefonnummer" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Epost-adresser" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Följande epost-adresser är knutna till ditt konto:" #: templates/account/email.html:25 msgid "Verified" msgstr "Verifierad" #: templates/account/email.html:29 msgid "Unverified" msgstr "Ej verifierad" #: templates/account/email.html:34 msgid "Primary" msgstr "Primär" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Gör primär" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Sänd verifiering igen" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Ta bort" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Lägg till epost-adress" #: templates/account/email.html:70 msgid "Add Email" msgstr "Lägg till epost" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Vill du verkligen ta bort den valda epost-adressen?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Du får det här mailet eftersom du eller någon annan försökte registrera ett\n" "konto med epost-adressen:\n" "\n" "%(email)s\n" "\n" "Men det finns redan ett konto med den epost-adressen. Om du har\n" "glömt detta, använd proceduren för glömt lösenord för att återställa\n" "ditt konto:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Konto finns redan" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Hej från %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Tack för att du använder %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "Du får det här mailet eftersom följande ändring gjordes på ditt konto:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Om du inte känner igen denna ändring, vidta lämpliga säkerhetsåtgärder " "omedelbart. Ändringen av ditt konto kommer från:\n" "\n" "- IP-adress: %(ip)s\n" "- Webbläsare: %(user_agent)s\n" "- Datum: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Din epost har ändrats från %(from_email)s till %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Epost ändrad" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Din epost har bekräftats." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Epost-bekräftelse" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Du får det här mailet eftersom användaren %(user_display)s har angett din " "epost-adress för att registrera ett konto på %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Din epost-verifieringskod anges nedan. Vänligen ange den i ditt öppna " "webbläsarfönster." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "För att bekräfta att detta stämmer, gå till %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Vänligen bekräfta din epost-adress" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "Epost-adressen %(deleted_email)s har tagits bort från ditt konto." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Epost borttagen" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Din inloggningskod anges nedan. Vänligen ange den i ditt öppna " "webbläsarfönster." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "Detta mail kan ignoreras om du inte initierade denna åtgärd." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Inloggningskod" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Ditt lösenord har ändrats." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Lösenord ändrat" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Din kod för lösenordsåterställning anges nedan. Vänligen ange den i ditt " "öppna webbläsarfönster." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Kod för lösenordsåterställning" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Du får det här mailet eftersom du eller någon annan har begärt en " "lösenordsåterställning för ditt användarkonto.\n" "Du kan bortse från detta mail om du inte begärt en återställning. Klicka på " "länken nedan för att återställa ditt lösenord." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Ditt användarnamn är %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Återställning av lösenord" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Ditt lösenord har återställts." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Ditt lösenord har ställts in." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Lösenord inställt" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Du får det här mailet eftersom du, eller någon annan, försökte komma åt ett " "konto med epost %(email)s. Vi har dock ingen uppgift om ett sådant konto i " "vår databas." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "Om det var du kan du registrera ett konto med länken nedan." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Okänt konto" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Epost-adress" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Nuvarande epost" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Ändras till" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Din epost-adress väntar fortfarande på verifiering." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Avbryt ändring" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Ändra till" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Ändra epost" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Verifiera epost-adress" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Verifiera att %(email)s är en epost-adress " "för %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Kan inte bekräfta %(email)s eftersom den redan är bekräftad av ett annat " "konto." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Länken för att verifiera epost-adressen har förfallit eller är ogiltig. Skapa en ny epost-verification." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "Har du redan ett konto? Då kan du %(link)slogga in%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Logga in med en nyckel" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Skicka mig en inloggningskod" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Logga ut" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Är du säker att du vill logga ut?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Du kan inte ta bort din primära epost-adress (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Verifierings-mail skickat till %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Du har verifierat %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Ta bort epost-adress %(email)s." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Du har loggat in som %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Du har loggat ut." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "En inloggningskod har skickats till %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Lösenordet ändrat." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Lösenord skapat." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "En verifieringskod har skickats till %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Du har verifierat telefonnummer %(phone)s." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Primär epost-adress satt." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Ändra lösenord" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Glömt lösenordet?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Glömt ditt lösenord? Ange din epost-adress nedan så skickar vi ett mail med " "instruktioner för att återställa det." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Återställ mitt lösenord" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Vänligen kontakta oss om du har problem med att återställa ditt lösenord." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Vi har skickat dig ett mail. Om du inte har fått det, kontrollera din " "skräppost. Kontakta oss om du inte får det inom ett par minuter." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Felaktig nyckel" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Länken för att återställa lösenordet var ogiltig, möjligtvis för att den " "redan har använts. Begär en ny lösenords-" "återställning." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Ditt lösenord är nu ändrat." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Skapa lösenord" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Ändra telefon" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Nuvarande telefon" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Ditt telefonnummer väntar fortfarande på verifiering." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Ändra telefon" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Ange ditt lösenord:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Du kommer att få en speciell kod för en lösenordsfri inloggning." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Begär kod" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Andra inloggningsalternativ" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Skapa konto" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Skapa konto" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "Har du redan ett konto? Då kan du %(link)slogga in%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Registrera dig med en nyckel" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Registrera med nyckel" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Andra alternativ" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Anmälan stängd" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Tyvärr är anmälan stängd för närvarande." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Information" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Du är redan inloggad som %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Varning:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Du har inte angett någon epost-adress. Du borde lägga till en epost-adress " "så att du kan få meddelanden, återställa ditt lösenord och liknande." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Verifiera din epost-adress" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Vi har skickat ett mail till dig för verifiering. Följ länken för att " "slutföra registreringen. Om du inte ser verifieringsmailet i din inkorg, " "kontrollera din skräppost. Kontakta oss om du inte får verifieringsmailet " "inom ett par minuter." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "För att få tillgång till denna del av webbplatsen måste vi verifiera att\n" "du är den du säger att du är. Därför måste du\n" "verifiera att du äger din epost-adress. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Vi har skickat ett mail till dig för\n" "verifiering. Vänligen klicka på länken i mailet. Om du inte ser " "verifieringsmailet i din inkorg, kontrollera din skräppost. Annars\n" "kontakta oss om du inte får det inom ett par minuter." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Information: du kan fortfarande ändra din epost-adress." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Meddelanden:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Meny:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Anslutna tredjeparts-konton" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Tvåfaktorsautentisering" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Sessioner" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Auktorisera" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s vill komma åt ditt %(site_name)s-konto." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Ange enhetskod" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Ange koden som visas på din enhet." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Fortsätt" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Bekräfta enhet" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Vänligen bekräfta koden som visas på din %(client_name)s för att auktorisera " "denna enhet." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Neka" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Enhet auktoriserad" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Du har auktoriserat din %(client_name)s-enhet." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Enhet nekad" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Auktorisering för din %(client_name)s-enhet har nekats." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Fel" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Förbli inloggad" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Ditt konto är skyddat av tvåfaktorsautentisering. Vänligen ange en " "autentiseringskod:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "En ny uppsättning återställningskoder för tvåfaktorsautentisering har " "genererats." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Nya återställningskoder genererade" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Autentiseringsapp aktiverad." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Autentiseringsapp aktiverad" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Autentiseringsapp inaktiverad." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Autentiseringsapp inaktiverad" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "En ny säkerhetsnyckel har lagts till." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Säkerhetsnyckel tillagd" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "En säkerhetsnyckel har tagits bort." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Säkerhetsnyckel borttagen" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Autentiseringsapp" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Autentisering med en autentiseringsapp är aktiv." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Ingen autentiseringsapp är aktiv." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Inaktivera" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Aktivera" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Säkerhetsnycklar" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Du har lagt till %(count)s säkerhetsnyckel." msgstr[1] "Du har lagt till %(count)s säkerhetsnycklar." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Inga säkerhetsnycklar har lagts till." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Hantera" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Lägg till" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Återställningskoder" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "Det finns %(unused_count)s av %(total_count)s återställningskoder " "tillgängliga." msgstr[1] "" "Det finns %(unused_count)s av %(total_count)s återställningskoder " "tillgängliga." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Inga återställningskoder har ställts in." #: templates/mfa/index.html:96 msgid "View" msgstr "Visa" #: templates/mfa/index.html:102 msgid "Download" msgstr "Ladda ner" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Generera" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "En ny uppsättning återställningskoder har genererats." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Säkerhetsnyckel tillagd." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Säkerhetsnyckel borttagen." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Ange en autentiseringskod:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "" "Du håller på att generera en ny uppsättning återställningskoder för ditt " "konto." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Denna åtgärd kommer att ogiltigförklara dina befintliga koder." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Är du säker?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Oanvända koder" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Ladda ner koder" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Generera nya koder" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Aktivera autentiseringsapp" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "För att skydda ditt konto med tvåfaktorsautentisering, skanna QR-koden nedan " "med din autentiseringsapp. Ange sedan verifieringskoden som genereras av " "appen." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Autentiseringshemlighet" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Du kan spara denna hemlighet och använda den för att installera om din " "autentiseringsapp vid ett senare tillfälle." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Inaktivera autentiseringsapp" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Du håller på att inaktivera autentisering via autentiseringsapp. Är du säker?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Lita på denna webbläsare?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Om du väljer att lita på denna webbläsare kommer du inte att behöva ange en " "verifieringskod nästa gång du loggar in." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Lita på i %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Lita inte" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Lägg till säkerhetsnyckel" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Ta bort säkerhetsnyckel" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Är du säker på att du vill ta bort denna säkerhetsnyckel?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Användning" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Nyckel" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Säkerhetsnyckel" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Denna nyckel anger inte om den är en passkey." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Ospecificerad" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Tillagd %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Senast använd %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Redigera" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Redigera säkerhetsnyckel" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Spara" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Skapa nyckel" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Du håller på att skapa en nyckel för ditt konto. Eftersom du kan lägga till " "fler nycklar senare kan du använda ett beskrivande namn för att skilja dem " "åt." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Skapa" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Denna funktion kräver JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Fel vid tredjeparts-inloggning" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "Ett fel inträffade vid inloggning via ditt tredjeparts-konto." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Du kan logga in på ditt konto via något av följande tredjeparts-konton:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Du har för närvarande inga tredjeparts-konton knutna till ditt konto." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Lägg till tredjeparts-konto" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "Ett tredjeparts-konto från %(provider)s har anslutits till ditt konto." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Tredjeparts-konto anslutet" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" "Ett tredjeparts-konto från %(provider)s har kopplats bort från ditt konto." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Tredjeparts-konto bortkopplat" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Anslut %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Du håller på att ansluta ett nytt tredjeparts-konto från %(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Logga in via %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "Du håller på att logga in med ett tredjeparts-konto från %(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Inloggning avbruten" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Du valde att avbryta inloggningen via ett av dina tredjeparts-konton. Om " "detta var ett misstag kan du logga in igen." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Tredjeparts-kontot har anslutits." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Tredjeparts-kontot har kopplats bort." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Du håller på att logga in via ditt %(provider_name)s-konto på\n" "%(site_name)s. Fyll i följande formulär för att slutföra:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Eller använd en tredje part" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Utloggad från alla andra sessioner." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Startad" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP-adress" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Webbläsare" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Senast sedd" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Nuvarande" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Logga ut andra sessioner" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Användarsessioner" #: usersessions/models.py:94 msgid "session key" msgstr "sessionsnyckel" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Anslutna tredjeparts-konton" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Lösenordet måste vara minst {0} tecken långt" #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Du får det här mailet eftersom du eller någon annan har begärt ett " #~ "lösenord för ditt konto. Dock har vi inget konto kopplat till e-" #~ "postadressen %(email)s.\n" #~ "\n" #~ "Om det var du kan du registrera ett konto med länken nedan." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "Följande epost-adresser är knutna till ditt konto:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "Verifiera epost-adress" #, fuzzy, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "Logga in med ett\n" #~ "av dina befintliga tredjeparts-konton eller skapa ett konto \n" #~ "för %(site_name)s och logga in nedan:" #~ msgid "or" #~ msgstr "eller" #~ msgid "change password" #~ msgstr "ändra lösenord" #~ msgid "OpenID Sign In" #~ msgstr "Inloggning via OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "Denna epost-adress är redan knuten till ett annat konto" #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Ett mail har nu skickats. Kontakta oss om det inte dyker upp inom ett par " #~ "minuter." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "Inloggningen och/eller lösenordet är felaktigt." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "" #~ "Användarnamn kan endast innehålla bokstäver, siffror samt @/./+/-/_." #~ msgid "This username is already taken. Please choose another." #~ msgstr "Användarnamnet är upptaget. Välj ett annat." #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "Du har verifierat att %(email)s är en " #~ "epost-adress för %(user_display)s." #~ msgid "Thanks for using our site!" #~ msgstr "Tack för att du använder vår hemsida!" #~ msgid "Confirmation email sent to %(email)s" #~ msgstr "Epost-bekräftelse skickad till %(email)s" #~ msgid "Delete Password" #~ msgstr "Ta bort lösenordet" #~ msgid "" #~ "You may delete your password since you are currently logged in using " #~ "OpenID." #~ msgstr "Du kan ta bort ditt lösenord eftersom du är inloggad via OpenID." #~ msgid "delete my password" #~ msgstr "ta bort mitt lösenord" #~ msgid "Password Deleted" #~ msgstr "Lösenordet borttaget" ================================================ FILE: allauth/locale/th/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: # Nattaphoom Chaipreecha, 2015 # msgid "" msgstr "" "Project-Id-Version: django-allauth\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2026-01-25 06:42+0000\n" "Last-Translator: Krit Autarawisead \n" "Language-Team: Thai \n" "Language: th\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 5.16-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "บัญชีนี้อยู่ในสถานะที่ใช้งานไม่ได้" #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "คุณไม่สามารถลบที่อยู่อีเมลหลักของคุณได้" #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "อีเมลนี้ได้ถูกเชื่อมกับบัญชีนี้แล้ว" #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "อีเมลและ/หรือรหัสผ่านที่ระบุมาไม่ถูกต้อง" #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "เบอร์โทรศัพท์และ/หรือรหัสผ่านที่ระบุมาไม่ถูกต้อง" #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "ชื่อผู้ใช้ได้ถูกลงทะเบียนด้วยอีเมลนี้แล้ว" #: account/adapter.py:75 msgid "Please type your current password." msgstr "โปรดใส่รหัสผ่านปัจจุบัน" #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "รหัสไม่ถูกต้อง" #: account/adapter.py:77 msgid "Incorrect password." msgstr "รหัสปัจจุบันรหัสผ่านไม่ถูกต้อง" #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "รหัสไม่ถูกต้องหรือหมดอายุ" #: account/adapter.py:79 msgid "Invalid login." msgstr "ล็อกอินไม่ถูกต้อง" #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "token ที่ใช้รีเซ็ทรหัสผ่านไม่ถูกต้อง" #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "บัญชีของคุณไม่มีการยืนยัน ไม่สามารถเพิ่มที่อยู่อีเมลมากกว่า %d รายการ" #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "เบอร์โทรศัพท์นี้ถูกลงทะเบียนแล้ว" #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "พยายามเข้าสู่ระบบล้มเหลวหลายครั้งเกินไป ลองอีกครั้งในภายหลัง" #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "อีเมลนี้ไม่ได้ผูกกับบัญชีผู้ใช้ใดเลย" #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "เบอร์โทรศัพท์นี้ไม่ได้ผูกกับบัญชีผู้ใช้ใดเลย" #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "อีเมลหลักของคุณต้องได้การยืนยัน" #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "ไม่สามารถใช้ชื่อผู้ใช้นี้ได้ กรุณาใช้ชื่อผู้ใช้อื่น" #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "ชื่อผู้ใช้และ/หรือรหัสผ่านที่ระบุมาไม่ถูกต้อง" #: account/adapter.py:98 msgid "Please select only one." msgstr "กรุณาเลือกเพียงหนึ่งตัวเลือก" #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "ค่าใหม่ต้องแตกต่างจากค่าปัจจุบัน" #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "กรุณาอดทน คุณกำลังส่งคำขอมากเกินไป" #: account/adapter.py:826 msgid "Use your password" msgstr "ใช้รหัสผ่านของคุณ" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "ใช้แอปหรือรหัสยืนยันตัวตน" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "ใช้คีย์ความปลอดภัย" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "ทำเครื่องหมาย {email} ว่ายืนยันแล้ว" #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "ไม่สามารถทำเครื่องหมาย {email} ว่ายืนยันแล้ว" #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "อีเมลหลักของคุณต้องได้การยืนยัน" #: account/apps.py:11 msgid "Accounts" msgstr "บัญชี" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "อีเมล" #: account/fields.py:19 msgid "Email address" msgstr "อีเมล" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "ป้อนหมายเลขโทรศัพท์พร้อมรหัสประเทศ (เช่น +1 สำหรับสหรัฐอเมริกา)" #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "โทรศัพท์" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "ต้องพิมพ์รหัสผ่านเดิมซ้ำอีกครั้ง" #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "รหัสผ่าน" #: account/forms.py:67 msgid "Remember Me" msgstr "จดจำการเข้าใช้" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "ชื่อผู้ใช้" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "ลงชื่อเข้าใช้" #: account/forms.py:115 msgid "Username, email or phone" msgstr "ชื่อผู้ใช้ อีเมล หรือเบอร์โทรศัพท์" #: account/forms.py:117 msgid "Username or email" msgstr "ชื่อผู้ใช้ หรือ อีเมล" #: account/forms.py:119 msgid "Username or phone" msgstr "ชื่อผู้ใช้ หรือเบอร์โทรศัพท์" #: account/forms.py:121 msgid "Email or phone" msgstr "อีเมล หรือเบอร์โทรศัพท์" #: account/forms.py:144 msgid "Forgot your password?" msgstr "ลืมรหัสผ่านของคุณ?" #: account/forms.py:287 msgid "Email (again)" msgstr "อีเมล์ (ไม่บังคับ)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "การยืนยันที่อยู่อีเมล" #: account/forms.py:302 msgid "Email (optional)" msgstr "อีเมล (ไม่จำเป็น)" #: account/forms.py:314 msgid "Username (optional)" msgstr "ชื่อผู้ใช้ (ไม่จำเป็น)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "คุณจะต้องพิมพ์รหัสผ่านอีเมลเดียวกันทุกครั้ง" #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "รหัสผ่าน (อีกครั้ง)" #: account/forms.py:591 msgid "Current Password" msgstr "รหัสผ่านปัจจุบัน" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "รหัสผ่านใหม่" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "รหัสผ่านใหม่ (อีกครั้ง)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "รหัส" #: account/models.py:23 msgid "user" msgstr "ผู้ใช้" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "อีเมล" #: account/models.py:31 msgid "verified" msgstr "ยืนยันแล้ว" #: account/models.py:32 msgid "primary" msgstr "หลัก" #: account/models.py:38 msgid "email addresses" msgstr "อีเมล" #: account/models.py:142 msgid "created" msgstr "สร้างแล้ว" #: account/models.py:143 msgid "sent" msgstr "ส่งแล้ว" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "คีย์" #: account/models.py:149 msgid "email confirmation" msgstr "การยืนยันอีเมล" #: account/models.py:150 msgid "email confirmations" msgstr "การยืนยันอีเมล" #: headless/apps.py:7 msgid "Headless" msgstr "หัวขาด" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "ดูรหัสผู้ใช้ของคุณ" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "ดูที่อยู่อีเมลของคุณ" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "ดูข้อมูลโปรไฟล์พื้นฐานของคุณ" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "อนุญาตสิทธิ์" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "ไม่อนุญาตให้ใช้ไวลด์การ์ด เว้นแต่จะเปิดใช้งาน 'อนุญาตไวลด์การ์ด URI'" #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' มีไวลด์การ์ด (*) มากกว่าหนึ่งตัว อนุญาตให้ใช้ไวลด์การ์ดได้เพียงหนึ่งตัวต่อ URI เท่านั้น" #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "อนุญาตให้ใช้ไวลด์การ์ดได้เฉพาะในส่วนชื่อโฮสต์ของ URI เท่านั้น" #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "รหัสการอนุญาต" #: idp/oidc/models.py:38 msgid "Device code" msgstr "รหัสอุปกรณ์" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "ข้อมูลรับรองไคลเอนต์" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "โทเค็นรีเฟรช" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "ความลับ" #: idp/oidc/models.py:44 msgid "Public" msgstr "สาธารณะ" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "ขอบเขตที่ไคลเอนต์ได้รับอนุญาตให้ขอ ระบุหนึ่งค่าต่อหนึ่งบรรทัด เช่น: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "ในกรณีที่ไคลเอนต์ไม่ระบุขอบเขต จะใช้ขอบเขตเริ่มต้นเหล่านี้ ระบุหนึ่งค่าต่อหนึ่งบรรทัด เช่น: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "รายการประเภทการอนุญาตที่ได้รับอนุญาต ระบุหนึ่งค่าต่อหนึ่งบรรทัด เช่น: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "รายการต้นทางที่ได้รับอนุญาตสำหรับคำขอข้ามโดเมน หนึ่งรายการต่อหนึ่งบรรทัด" #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "อนุญาตให้ใช้ไวลด์การ์ด (*) ใน URI เปลี่ยนเส้นทางและ CORS origins เมื่อเปิดใช้งาน URI " "สามารถมีเครื่องหมายดอกจันหนึ่งตัวเพื่อจับคู่กับซับโดเมน" #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "รายการประเภทการตอบกลับที่ได้รับอนุญาต ระบุหนึ่งค่าต่อหนึ่งบรรทัด เช่น: code(ENTER)id_token " "token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "ไคลเอนต์" #: idp/oidc/models.py:116 msgid "clients" msgstr "ไคลเอนต์" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "คุณไม่สามารถเพิ่มที่อยู่อีเมลลงในบัญชีที่ได้รับการป้องกันโดยการรับรองความถูกต้องด้วยสองปัจจัย" #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "คุณไม่สามารถปิดใช้งานการรับรองความถูกต้องด้วยสองปัจจัยได้" #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "คุณไม่สามารถสร้างรหัสกู้คืนได้โดยไม่ต้องเปิดใช้งานการรับรองความถูกต้องด้วยสองปัจจัย" #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "คุณไม่สามารถเปิดใช้งานการรับรองความถูกต้องด้วยสองปัจจัยได้จนกว่าคุณจะยืนยันที่อยู่อีเมลของคุณแล้ว" #: mfa/adapter.py:141 msgid "Master key" msgstr "มาสเตอร์คีย์" #: mfa/adapter.py:143 msgid "Backup key" msgstr "คีย์สำรอง" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "หมายเลขคีย์ {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA" #: mfa/models.py:24 msgid "Recovery codes" msgstr "รหัสการกู้คืน" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "เครื่องยืนยันตัวตน TOTP" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "รหัสตรวจสอบความถูกต้อง" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "ไร้รหัสผ่าน" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "การเปิดใช้งานการทำงานแบบไร้รหัสผ่านทำให้คุณสามารถลงชื่อเข้าใช้โดยใช้เพียงคีย์นี้ " "แต่ต้องมีข้อกำหนดเพิ่มเติม เช่น ไบโอเมตริกซ์หรือการป้องกัน PIN" #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "มีบัญชีที่ใช้อีเมลนี้แล้ว โปรดลงชื่อเข้าใช้บัญชีนั้นก่อน จากนั้นเชื่อมต่อกับบัญชี %s ของคุณ" #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "โทเค็นไม่ถูกต้อง TInvalid" #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "บัญชีของคุณไม่ได้ตั้งรหัสผ่านไว้" #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "บัญชีของคุณไม่มีอีเมลที่ยืนยันแล้ว" #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "" "คุณสามารถลงชื่อเข้าใช้บัญชีของคุณได้โดยใช้วิธีต่อไปนี้: " "ไม่ตัดการเชื่อมต่อบัญชีบุคคลที่สามที่เหลืออยู่ล่าสุดของคุณ:" #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "บัญชีบุคคลที่สามของ Social เชื่อมโยงกับบัญชีอื่นอยู่แล้ว" #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "บัญชีโซเชียล" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "ผู้ให้บริการ" #: socialaccount/models.py:53 msgid "provider ID" msgstr "ผู้ให้บริการ ไอดี" #: socialaccount/models.py:57 msgid "name" msgstr "ชื่อ" #: socialaccount/models.py:59 msgid "client id" msgstr "รหัสลูกค้า" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "รหัสแอปหรือรหัสผู้บริโภค" #: socialaccount/models.py:64 msgid "secret key" msgstr "รหัสลับ" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "ความลับของ API, ความลับของไคลเอ็นต์ หรือความลับของผู้บริโภค" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "คีย์" #: socialaccount/models.py:82 msgid "social application" msgstr "การประยุกต์ใช้ทางสังคม" #: socialaccount/models.py:83 msgid "social applications" msgstr "การประยุกต์ใช้ทางสังคม" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "เข้าสู่ระบบครั้งล่าสุด" #: socialaccount/models.py:121 msgid "date joined" msgstr "วันที่เข้าร่วม" #: socialaccount/models.py:122 msgid "extra data" msgstr "ข้อมูลเพิ่มเติม" #: socialaccount/models.py:126 msgid "social account" msgstr "บัญชีโซเชียล" #: socialaccount/models.py:127 msgid "social accounts" msgstr "บัญชีโซเชียล" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) หรือโทเค็นการเข้าถึง (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "ความลับของโทเค็น" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) หรือรีเฟรชโทเค็น (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "หมดอายุเมื่อ" #: socialaccount/models.py:175 msgid "social application token" msgstr "โทเค็นแอปพลิเคชันโซเชียล" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "โทเค็นแอปพลิเคชันโซเชียล" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "ข้อมูลโปรไฟล์ไม่ถูกต้อง" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "ลงชื่อเข้าใช้" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "ยกเลิก" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "การตอบสนองไม่ถูกต้องขณะได้รับคำขอโทเค็นจาก \"%s\" การตอบสนองคือ: %s" #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "การตอบสนองผิดพลาดขณะที่กำลังได้รับ access token จาก \"%s\"" #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "ไม่มีการบันทึก request token ของ \"%s\"" #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "ไม่มีการบันทึก access token ของ \"%s\"" #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "ไม่มีสิทธิ์การเข้าใช้ทรัพยากรส่วนตัว ที่ \"%s\"" #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "การตอบสนองผิดพลาดขณะที่กำลังได้รับ request token จาก \"%s\"" #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "บัญชีไม่มีการใช้งาน" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "บัญชีนี้ไม่มีการใช้งาน" #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "เราส่งรหัสไปที่ %(recipient)s รหัสจะหมดอายุในไม่ช้า โปรดกรอกรหัสโดยเร็ว" #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "ยืนยัน" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "ขอรหัสใหม่" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "ยืนยันที่อยู่อีเมล AddrAccess" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "โปรดตรวจสอบสิทธิ์อีกครั้งเพื่อปกป้องบัญชีของคุณ" #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "ทางเลือกอื่น" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "ยืนยันอีเมล์" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "กรอกรหัสยืนยันอีเมล" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "ใช้อีเมลอื่น" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "ลงชื่อเข้าใช้" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "ป้อนรหัสเข้าสู่ระบบ" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "รีเซ็ทรหัสผ่าน" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "กรอกรหัสรีเซ็ตรหัสผ่าน" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "ยืนยันเบอร์โทรศัพท์" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "กรอกรหัสยืนยันเบอร์โทรศัพท์" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "ใช้หมายเลขโทรศัพท์อื่น" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "อีเมล" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "อีเมลต่อไปนี้ ได้เชื่อมกับบัญชีของคุณ" #: templates/account/email.html:25 msgid "Verified" msgstr "ยืนยันแล้ว" #: templates/account/email.html:29 msgid "Unverified" msgstr "ยังไม่ได้ยืนยัน" #: templates/account/email.html:34 msgid "Primary" msgstr "หลัก" #: templates/account/email.html:44 msgid "Make Primary" msgstr "ทำให้เป็นอันหลัก" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "ส่งการยืนยันอีกครั้ง" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "ลบ" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "เพิ่มอีเมล" #: templates/account/email.html:70 msgid "Add Email" msgstr "เพิ่มอีเมล" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "คุณต้องการที่จะลบอีเมลนี้จริงหรอ" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "คุณได้รับอีเมลนี้ เพราะคุณหรือมีคนอื่นพยายามลงทะเบียน\n" "บัญชีโดยใช้ที่อยู่อีเมล:\n" "\n" "%(email)s\n" "\n" "อย่างไรก็ตาม มีบัญชีที่ใช้อีเมลดังกล่าวอยู่แล้ว ในกรณีที่คุณ\n" "ลืมอีเมลนี้ไป โปรดใช้ขั้นตอนลืมรหัสผ่านเพื่อกู้คืน\n" "บัญชีของคุณ:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "มีบัญชีอยู่แล้ว" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "สวัสดีจาก %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "ขอบคุณที่ใช้บริการของ %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "คุณได้รับอีเมลนี้ เนื่องจากมีการเปลี่ยนแปลงต่อไปนี้กับบัญชีของคุณ" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "หากคุณไม่รู้จักการเปลี่ยนแปลงนี้ โปรดดำเนินการตามมาตรการรักษาความปลอดภัยที่เหมาะสมทันที " "การเปลี่ยนแปลงในบัญชีของคุณมีต้นตอมาจาก:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "อีเมล์ของคุณถูกเปลี่ยนจาก %(from_email)s to %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "ที่อยู่อีเมลเปลี่ยนแปลง" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "คุณได้ทำการยืนยัน %(email)s" #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "การยืนยันทางอีเมล์" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "คุณได้รับอีเมลนี้เพราะผู้ใช้ %(user_display)s ให้ที่อยู่อีเมลของคุณเพื่อลงทะเบียนบัญชีใน " "%(site_domain)s" #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "รหัสยืนยันอีเมลของคุณแสดงอยู่ด้านล่างนี้ โปรดป้อนรหัสดังกล่าวในหน้าต่างเบราว์เซอร์ที่เปิดอยู่" #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "เพื่อยืนยันว่าถูกต้อง โปรดไปที่ %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "กรุณายืนยันอีเมลของคุณ" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "ที่อยู่อีเมล %(deleted_email)s ถูกลบออกจากบัญชีของคุณ" #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "ลบ อีเมล์" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "รหัสลงชื่อเข้าใช้ของคุณแสดงอยู่ด้านล่างนี้ โปรดป้อนรหัสดังกล่าวในหน้าต่างเบราว์เซอร์ที่เปิดอยู่" #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "คุณสามารถละเว้นเมลนี้ได้อย่างปลอดภัยหากคุณไม่ได้เป็นผู้เริ่มดำเนินการนี้" #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "ลงชื่อเข้าใช้" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "รหัสผ่านของคุณได้เปลี่ยนแล้ว" #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "รหัสผ่าน (อีกครั้ง)" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "รหัสรีเซ็ตรหัสผ่านของคุณแสดงอยู่ด้านล่างนี้ โปรดป้อนรหัสดังกล่าวในหน้าต่างเบราว์เซอร์ที่เปิดอยู่" #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "รหัสรีเซ็ตรหัสผ่าน" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "สวัสดีจาก %(site_name)s!\n" "\n" "You're receiving this e-mail because you or someone else has requested a " "คุณได้รับอีเมลนี้เพราะว่า คุณหรือใครบางคนได้ทำการร้องขอรหัสผ่านของบัญชีนี้ที่ %(site_domain)s.\n" "It can be safely ignored if you did not request a password reset. Click the " "คุณสามารถมองข้ามและลบอีเมลนี้ทิ้งได้เลยหากคุณไม่ได้ทำการร้องขอการรีเซ็ทรหัสผ่านคลิกที่ลิงค์ข้างล่างนี้เพื่อรีเซ็ทรหัสผ่านของคุณ" #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "ในกรณีเผื่อคุณลืม ชื่อผู้ใช้ของคุณคือ %(username)s" #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "อีเมลในการรีเซ็ทรหัสผ่าน" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "รหัสผ่านของคุณได้ถูกเปลี่ยนและรีเซ็ตแล้ว" #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "รหัสผ่านของคุณได้เปลี่ยนแล้ว" #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "รีเซ็ทรหัสผ่าน" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "คุณได้รับอีเมลนี้เนื่องจากคุณหรือบุคคลอื่นพยายามเข้าถึงบัญชีที่มีอีเมล %(email)s อย่างไรก็ตาม " "เราไม่มีบันทึกเกี่ยวกับบัญชีดังกล่าวในฐานข้อมูลของเรา" #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "หากเป็นคุณ คุณสามารถลงทะเบียนบัญชีโดยใช้ลิงก์ด้านล่าง" #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "ไม่ทราบ บัญชี" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "ที่อยู่อีเมล" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "รหัสผ่านอีเมลปัจจุบัน" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "เปลี่ยนเป็น" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "ที่อยู่อีเมลหลักของคุณยังต้องอยู่ระหว่างรอการยืนยัน" #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "ยกเลิกการเปลี่ยนแปลง" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "อีเมล์เปลี่ยนเป็น" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "เปลี่ยนอีเมล์" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "ยืนยันอีเมล" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "กรุณายืนยันว่า %(email)s เป็นที่อยู่อีเมลของผู้ใช้ " "%(user_display)s" #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "ไม่สามารถยืนยัน %(email)s ได้เพราะได้รับการยืนยันโดยบัญชีอื่นแล้ว" #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "ลิงก์ยืนยันอีเมลนี้หมดอายุหรือไม่ถูกต้อง กรุณา ส่งคำขอยืนยันอีเมลใหม่" #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "ถ้าหากคุณยังไม่มีบัญชี, กรุณา %(link)sลงทะเบียน%(end_link)sก่อน." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "ลงชื่อเข้าใช้ด้วยรหัสผ่าน" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "ส่งรหัสลงชื่อเข้าใช้ให้ฉัน" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "ลงชื่อออก" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "คุณแน่ใจหรือว่าต้องการจะลงชื่อออก" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "คุณไม่สามารถลบอีเมลของคุณได้ (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "อีเมลยืนยันได้ถูกส่งไปที่ %(email)s" #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "คุณได้ทำการยืนยัน %(email)s" #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "ลบอีเมล %(email)s แล้ว" #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "%(name)s ลงชื่อเข้าใช้สำเร็จ" #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "คุณได้ออกจากระบบแล้ว" #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "รหัสลงชื่อเข้าใช้ได้ถูกส่งไปยัง %(recipient)s แล้ว" #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "เปลี่ยนรหัสผ่านสำเร็จ" #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "ตั้งรหัสผ่านสำเร็จ" #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "รหัสยืนยันได้ถูกส่งไปยัง %(phone)s แล้ว" #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "คุณได้ยืนยันหมายเลขโทรศัพท์ %(phone)s แล้ว" #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "ตั้งอีเมลหลักสำเร็จ" #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "เปลี่ยนรหัสผ่าน" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "ลืมรหัสผ่าน?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "ลืมรหัสผ่านหรือไม่? ป้อนที่อยู่อีเมลของคุณด้านล่าง และเราจะส่งอีเมลไปให้คุณเพื่อรีเซ็ตรหัสผ่าน" #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "รีเซ็ทรหัสผ่านของฉัน" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "กรุณาติดต่อเราหากคุณพบปัญหาในการรีเซ็ทรหัสผ่าน" #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "เราได้ส่งอีเมลถึงคุณแล้ว หากต้องการยืนยันตัวตน โปรดคลิกลิงก์ในอีเมลนี้ โปรดไปที่โฟลเดอร์สแปม " "มิฉะนั้น โปรดติดต่อเราหากไม่ได้รับอีเมลภายในไม่กี่นาที" #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Token เสีย" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "ลิงค์ที่ใช้ในการรีเซ็ทรหัสผ่านไม่ถูกต้อง อาจเป็นไปได้ว่ามันได้ถูกใช้ไปแล้วกรุณาร้องขอ การรีเซ็ทรหัสผ่านใหม่" #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "รหัสผ่านของคุณได้เปลี่ยนแล้ว" #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "ตั้งรหัสผ่าน" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "เปลี่ยนเบอร์โทรศัพท์" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "เบอร์โทรศัพท์ปัจจุบัน" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "เบอร์โทรศัพท์ของคุณยังอยู่ระหว่างรอการยืนยัน" #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "เปลี่ยนเบอร์โทรศัพท์" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "ลืมรหัสผ่าน?:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "คุณจะได้รับรหัสพิเศษสำหรับการลงชื่อเข้าใช้แบบไม่ต้องใช้รหัสผ่าน" #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "ขอรหัส" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "ตัวเลือกการลงชื่อเข้าใช้อื่นๆ" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "ลงทะเบียน" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "ลงทะเบียน" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "มีบัญชีอยู่แล้ว? กรุณา%(link)sลงชื่อเข้าใช้%(end_link)s" #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "สมัครสมาชิกด้วยพาสคีย์" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "สมัครสมาชิกด้วยพาสคีย์" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "ตัวเลือกอื่นๆ" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "การลงทะเบียนปิดอยู่" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "เราขอโทษด้วย การลงทะเบียนได้ปิดชั่วคราว" #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "หมายเหตุ" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "คุณเข้าสู่ระบบแล้วในชื่อ %(user_display)s" #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "คำเตือน:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "ขณะนี้คุณยังไม่ได้ตั้งค่าที่อยู่อีเมล คุณควรเพิ่มที่อยู่อีเมลเพื่อรับการแจ้งเตือน รีเซ็ตรหัสผ่าน ฯลฯ" #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "ยืนยันอีเมลของคุณ" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "เราได้ส่งอีเมลถึงคุณเพื่อยืนยันแล้ว ทำตามลิงก์ที่ให้ไว้เพื่อดำเนินการลงทะเบียนให้เสร็จสิ้น " "หากคุณไม่พบอีเมลยืนยันในกล่องจดหมายหลัก โปรดตรวจสอบในโฟลเดอร์สแปม " "โปรดติดต่อเราหากคุณไม่ได้รับอีเมลยืนยันภายในไม่กี่นาที" #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "ส่วนนี้ของไซต์ต้องการให้เราตรวจสอบว่าคุณคือบุคคลที่คุณอ้างว่าเป็น เพื่อจุดประสงค์นี้ " "เราต้องการให้คุณ\n" "ยืนยันความเป็นเจ้าของที่อยู่อีเมลของคุณ " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "เราได้ส่งอีเมลถึงคุณเพื่อยืนยันแล้ว โปรดคลิกลิงก์ในอีเมลนี้ โปรดส่งอีเมล " "หากคุณไม่พบอีเมลยืนยันในกล่องจดหมายหลัก โปรดตรวจสอบในโฟลเดอร์สแปม มิฉะนั้น " "โปรดติดต่อเราหากไม่ได้รับอีเมลภายในไม่กี่นาที" #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "หมายเหตุ: คุณยังสามารถเปลี่ยนที่อยู่อีเมลของคุณได้" #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "ข้อความ:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "เมนู:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "การเชื่อมต่อบัญชีต่างๆ" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "การตรวจสอบสิทธิ์สองปัจจัย" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "เซสชั่น" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "อนุญาต" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s ต้องการเข้าถึงบัญชี %(site_name)s ของคุณ" #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "ป้อนรหัสอุปกรณ์" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "ป้อนรหัสที่แสดงบนอุปกรณ์ของคุณ" #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "ดำเนินการต่อ" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "ยืนยันอุปกรณ์" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "กรุณายืนยันรหัสที่แสดงบน %(client_name)s ของคุณเพื่ออนุญาตอุปกรณ์นี้" #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "ปฏิเสธ" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "อนุญาตอุปกรณ์แล้ว" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "คุณได้อนุญาตอุปกรณ์ %(client_name)s ของคุณสำเร็จแล้ว" #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "ปฏิเสธอุปกรณ์" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "การอนุญาตสำหรับอุปกรณ์ %(client_name)s ของคุณถูกปฏิเสธ" #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "ข้อผิดพลาด" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "คงสถานะลงชื่อเข้าใช้" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "บัญชีของคุณได้รับการปกป้องด้วยการตรวจสอบสิทธิ์สองขั้นตอน โปรดป้อนรหัสตรวจสอบสิทธิ์:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "มีการสร้างรหัสการกู้คืนการตรวจสอบสิทธิ์แบบสองปัจจัยชุดใหม่แล้ว" #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "รหัสการกู้คืนใหม่ถูกสร้างขึ้น" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "เปิดใช้งานแอปพลิเคชัน Authenticator แล้ว" #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "เปิดใช้งานแอป Authenticator แล้ว" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "ปิดใช้งานแอปพลิเคชัน Authenticator แล้ว" #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "ปิดใช้งานแอป Authenticator แล้ว" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "มีการเพิ่มคีย์ความปลอดภัยใหม่แล้ว" #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "เพิ่มรหัสความปลอดภัยแล้ว" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "คุณได้ยืนยันแล้วว่า %(email)sรหัสความปลอดภัยถูกลบออกแล้ว" #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "ถอดคีย์ความปลอดภัยออกแล้ว" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Authenticator แอป" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "การยืนยันตัวตนโดยใช้แอปพลิเคชันการยืนยันตัวตนเปิดใช้งานอยู่" #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "แอปพลิเคชันการตรวจสอบสิทธิ์ไม่ได้เปิดใช้งาน" #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "ปิดการใช้งาน" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "เปิดใช้งาน" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "กุญแจความปลอดภัย" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "คุณได้เพิ่มรหัสความปลอดภัย %(count)s แล้ว" #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "ไม่มีการเพิ่มคีย์ความปลอดภัย" #: templates/mfa/index.html:62 msgid "Manage" msgstr "จัดการ" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "แอป" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "รหัสการกู้คืน" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "มีรหัสกู้คืน %(unused_count)s จากทั้งหมด %(total_count)s รหัส" #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "ไม่มีการตั้งค่ารหัสการกู้คืน" #: templates/mfa/index.html:96 msgid "View" msgstr "ดู" #: templates/mfa/index.html:102 msgid "Download" msgstr "ดาวน์โหลด" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "สร้าง" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "มีการสร้างชุดรหัสการกู้คืนใหม่แล้ว" #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "เพิ่มคีย์ความปลอดภัยแล้ว" #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "ถอดคีย์ความปลอดภัยออกแล้ว" #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "กรอกรหัสยืนยันตัวตน:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "คุณกำลังจะสร้างชุดรหัสการกู้คืนใหม่สำหรับบัญชีของคุณ" #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "การดำเนินการนี้จะทำให้รหัสที่มีอยู่ของคุณไม่ถูกต้อง" #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "คุณแน่ใจมั้ย?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "รหัสที่ไม่ได้ใช้" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "รหัสดาวน์โหลด" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "สร้างรหัสใหม่" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "เปิดใช้งานแอป Authenticator" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "หากต้องการปกป้องบัญชีของคุณด้วยการตรวจสอบสิทธิ์สองขั้นตอน ให้สแกนรหัส QR " "ด้านล่างด้วยแอปตรวจสอบสิทธิ์ของคุณ จากนั้นป้อนรหัสยืนยันที่สร้างโดยแอปด้านล่าง" #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "ความลับของผู้รับรอง" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "คุณสามารถเก็บความลับนี้ไว้และนำไปใช้ในการติดตั้งแอปพลิเคชันการตรวจสอบความถูกต้องอีกครั้งในภายหลังได้" #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "ปิดใช้งานแอป Authenticator" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "คุณกำลังจะปิดการใช้งานการตรวจสอบสิทธิ์โดยใช้แอปตัวตรวจสอบสิทธิ์ คุณแน่ใจหรือไม่?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "ไว้วางใจเบราว์เซอร์นี้หรือไม่?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "หากคุณเลือกที่จะไว้วางใจเบราว์เซอร์นี้ คุณจะไม่ถูกขอรหัสยืนยันในครั้งถัดไปที่คุณเข้าสู่ระบบ" #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "ไว้วางใจเป็นเวลา %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "ไม่ไว้วางใจ" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "เพิ่มรหัสความปลอดภัย" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "ถอดกุญแจความปลอดภัยออก" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "คุณแน่ใจว่าต้องการลงชื่อออกหรือลบคีย์ความปลอดภัยนี้หรือไม่" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "การใช้งาน" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "กุญแจผี" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "กุญแจความปลอดภัย" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "คีย์นี้ไม่ได้ระบุว่าเป็นรหัสผ่านหรือไม่" #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "ไม่ระบุ" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "เพิ่มเมื่อ %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "ใช้ล่าสุด %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "แก้ไข" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "แก้ไขรหัสความปลอดภัย" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "บันทึก" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "สร้างพาสคีย์" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "คุณกำลังจะสร้างพาสคีย์สำหรับบัญชีของคุณ เนื่องจากคุณสามารถเพิ่มคีย์อื่นๆ ได้ในภายหลัง " "คุณสามารถใช้ชื่อที่อธิบายเพื่อแยกคีย์ออกจากกัน" #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "สร้าง" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "ฟังก์ชันนี้ต้องใช้ JavaScript" #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "การเข้าสู่ระบบของบุคคลที่สามในเครือข่ายสังคมออนไลน์ล้มเหลว" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "เกิดข้อผิดพลาดขณะพยายามเข้าสู่ระบบผ่านเครือข่ายโซเชียลหรือบัญชีบุคคลที่สามของคุณ" #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "คุณสามารถลงชื่อเข้าใช้บัญชีของคุณโดยใช้บัญชีบุคคลที่สามต่อไปนี้:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "ขณะนี้คุณไม่มีบัญชีบุคคลที่สามหรือเครือข่ายโซเชียลที่เชื่อมโยงกับบัญชีนี้" #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "เพิ่มบัญชีบุคคลที่สาม" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "บัญชีบุคคลที่สามจาก %(provider)s เชื่อมต่อกับบัญชีของคุณแล้ว" #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "เพิ่มบัญชีบุคคลที่สามที่เชื่อมต่อ" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "บัญชีบุคคลที่สามจาก %(provider)s ถูกตัดการเชื่อมต่อจากบัญชีของคุณ" #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "เพิ่มบัญชีบุคคลที่สามที่ถูกตัดการเชื่อมต่อ" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "เชื่อมต่อ %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "คุณกำลังจะเชื่อมต่อบัญชีบุคคลที่สามใหม่จาก %(provider)s" #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "ลงชื่อเข้าใช้ผ่าน %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "คุณกำลังจะลงชื่อเข้าใช้โดยใช้บัญชีบุคคลที่สามจาก %(provider)s" #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "การลงชื่เข้าใช้ยกเลิก" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "คุณตัดสินใจที่จะยกเลิกการลงชื่อเข้าใช้เว็บของเราด้วยหนึ่งในบัญชีของคุณถ้านี้เป็นความผิดพลาด กรุณาลงชื่อเข้าใช้" #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "บัญชีบุคคลที่สามของ Social ได้รับการเชื่อมโยงแล้ว" #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "บัญชีบุคคลที่สามของ Social ถูกตัดการเชื่อมต่อ" #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "คุณกำลังจะทำการใช้บัญชี %(provider_name)s ของคุณ ในการเข้าสู่ระบบของ\n" "%(site_name)s. ในขั้นตอนสุดท้าย กรุณากรอกฟอร์มข้างล่าง:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "หรือใช้บุคคลที่สาม" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "ออกจากระบบจากเซสชันอื่นทั้งหมด" #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "เริ่มต้นที่" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "อีเมล์ที่อยู่ IP" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "บราวเซอร์" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "พบเห็นครั้งสุดท้ายเมื่อ" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "รหัสผ่านปัจจุบัน" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "ออกจากระบบเซสชั่นอื่น ๆ" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "เซสชันผู้ใช้" #: usersessions/models.py:94 msgid "session key" msgstr "คีย์เซสชั่น" #~ msgid "Account Connection" #~ msgstr "การเชื่อมต่อบัญชี" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "รหัสผ่านต้องมีอย่างน้อย {0} ตัวอักษร" #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "สวัสดีจาก %(site_name)s!\n" #~ "\n" #~ "You're receiving this e-mail because you or someone else has requested a " #~ "คุณได้รับอีเมลนี้เพราะว่า คุณหรือใครบางคนได้ทำการร้องขอรหัสผ่านของบัญชีนี้ที่ " #~ "%(site_domain)s.\n" #~ "It can be safely ignored if you did not request a password reset. Click " #~ "the " #~ "คุณสามารถมองข้ามและลบอีเมลนี้ทิ้งได้เลยหากคุณไม่ได้ทำการร้องขอการรีเซ็ทรหัสผ่านคลิกที่ลิงค์ข้างล่างนี้เพื่อรีเซ็ทรหัสผ่านของคุณ" #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "อีเมลต่อไปนี้ได้เชื่อมกับบัญชีของคุณ" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "ยืนยันอีเมล" #, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "กรุณาลงชื่อเข้าใช้ด้วยบัญชีภายนอกของคุณ\n" #~ "หรือ Or, %(link)sลงทะเบียน %(end_link)s\n" #~ "สำหรับบัญชีของ %(site_name)s และลงชื่อเข้าใช้ด้านล่าง:" #~ msgid "or" #~ msgstr "sinv" #~ msgid "change password" #~ msgstr "เปลี่ยนรหัสผ่าน" #~ msgid "OpenID Sign In" #~ msgstr "ลงชื่อเข้าใช้ด้วย OpenID" #~ msgid "This email address is already associated with another account." #~ msgstr "อีเมลนี้ได้ถูกเชื่อมกับบัญชีอื่นแล้ว" #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "เราได้ส่งอีเมลให้คุณแล้ว. กรุณาติดต่อเราหากคุณไม่ได้รับอีเมลภายในเวลาไม่กี่นาทีนี้" #~ msgid "The login and/or password you specified are not correct." #~ msgstr "การลงชื่เข้าใช้และ/หรือรหัสผ่านที่ระบุมาไม่ถูกต้อง" #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "ชื่อผู้ใช้สามารถมีตัวอักษรภาษาอังกฤษตัวเลขและ @/./+/-/_. เท่านั้น" #~ msgid "This username is already taken. Please choose another." #~ msgstr "ชื่อผู้ใช้นี้ถูกใช้แล้ว กรุณาเลือกชื่ออื่น" #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "คุณได้ยืนยันว่า %(email)s เป็นอีเมลของผู้ใช้ %" #~ "(user_display)s." ================================================ FILE: allauth/locale/tr/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Translators: msgid "" msgstr "" "Project-Id-Version: django-allauth\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-07-25 20:27+0200\n" "Last-Translator: Emirhan Soytaş \n" "Language-Team: Turkish \n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.13-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Bu hesap şu anda etkin durumda değil." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Birincil e-posta adresinizi kaldıramazsınız." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "" "Bu e-posta adresi, halihazırda bu hesap ile ilişkilendirilmiş haldedir." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Girdiğiniz e-posta adresi ve/veya şifre doğru değil." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Girdiğiniz telefon numarası ve/veya şifre doğru değil." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Bu e-posta adresiyle bir kullanıcı zaten kayıtlı." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Lütfen mevcut şifrenizi giriniz." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Hatalı kod." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Hatalı şifre." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Geçersiz veya süresi geçmiş anahtar." #: account/adapter.py:79 msgid "Invalid login." msgstr "Geçersiz giriş." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Şifre sıfırlama kodu geçersiz." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "%d adetten fazla e-posta adresi ekleyemezsiniz." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Bu telefon numarası ile bir kullanıcı zaten kayıtlı." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Çok fazla hatalı giriş yapıldı. Lütfen daha sonra tekrar deneyin." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Bu e-posta adresi hiçbir kullanıcı hesabıyla ilişkili değil." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Bu telefon numarası hiçbir kullanıcı hesabıyla ilişkili değil." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Birincil e-posta adresinizin doğrulanması gerekmektedir." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "Bu kullanıcı adı kullanılamaz. Lütfen başka bir kullanıcı adı deneyin." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Girdiğiniz kullanıcı adı ve/veya şifre doğru değil." #: account/adapter.py:98 msgid "Please select only one." msgstr "Lütfen sadece bir adet seçin." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Yeni değer, geçerli değerden farklı olmalıdır." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Sabırlı olunuz, çok fazla istek göndermektesiniz." #: account/adapter.py:826 msgid "Use your password" msgstr "Şifrenizi kullanın" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Doğrulayıcı uygulaması veya kodu kullan" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Güvenlik anahtarı kullan" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} mail adresi, doğrulanmış olarak işaretlendi." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "{email} mail adresini doğrulanmış olarak işaretlerken hata alındı." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Seçili e-posta adreslerini doğrulanmış olarak işaretle" #: account/apps.py:11 msgid "Accounts" msgstr "Hesaplar" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "E-posta" #: account/fields.py:19 msgid "Email address" msgstr "E-posta adresi" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "Ülke kodu(ABD için +1 gibi, vb.) içeren bir telefon numarası giriniz." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefon Numarası" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Her sefer için aynı şifreyi girmelisiniz." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Şifre" #: account/forms.py:67 msgid "Remember Me" msgstr "Beni Hatırla" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Kullanıcı adı" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Giriş Yap" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Kullanıcı adı, e-posta adresi veya telefon numarası" #: account/forms.py:117 msgid "Username or email" msgstr "Kullanıcı adı ya da e-posta adresi" #: account/forms.py:119 msgid "Username or phone" msgstr "Kullanıcı adı veya telefon numarası" #: account/forms.py:121 msgid "Email or phone" msgstr "E-posta adresi veya telefon numarası" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Şifrenizi mi unuttunuz?" #: account/forms.py:287 msgid "Email (again)" msgstr "E-posta adresi (tekrar)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "E-posta adresi doğrulama" #: account/forms.py:302 msgid "Email (optional)" msgstr "E-posta adresi (zorunlu değil)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Kullanıcı adı (zorunlu değil)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Her sefer için aynı e-posta adresini girmelisiniz." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Şifre (tekrar)" #: account/forms.py:591 msgid "Current Password" msgstr "Mevcut Şifre" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Yeni Şifre" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Yeni Şifre (tekrar)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Kod" #: account/models.py:23 msgid "user" msgstr "kullanıcı" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "e-posta adresi" #: account/models.py:31 msgid "verified" msgstr "doğrulanmış" #: account/models.py:32 msgid "primary" msgstr "birincil" #: account/models.py:38 msgid "email addresses" msgstr "e-posta adresleri" #: account/models.py:142 msgid "created" msgstr "oluşturuldu" #: account/models.py:143 msgid "sent" msgstr "gönderildi" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "anahtar" #: account/models.py:149 msgid "email confirmation" msgstr "e-posta onayı" #: account/models.py:150 msgid "email confirmations" msgstr "e-posta onayları" #: headless/apps.py:7 msgid "Headless" msgstr "Gözetimsiz" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Kullanıcı ID'ni görüntüle" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "E-posta adresini görüntüle" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Temel profil bilgini görüntüle" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "İzin tanımla" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "'URI joker karakterlerine izin ver' etkinleştirilmediği sürece joker " "karakterlere izin verilmez." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' birden fazla joker karakter (*) içeriyor. Her URI için yalnızca bir " "joker karaktere izin verilir." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "" "Joker karakterlere yalnızca URI'nin ana bilgisayar adı kısmında izin verilir." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Yetki kodu" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Cihaz kodu" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "İstemci bilgileri" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Yenileme token'i" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Gizli" #: idp/oidc/models.py:44 msgid "Public" msgstr "Genel" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "İstemcinin istek atmaya izni olduğu kapsam(lar). Her satır için bir değer " "giriniz, örneğin: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "İstemcinin herhangi bir kapsam belirtmediği durumlarda bu varsayılan " "kapsamlar kullanılır. Her satır için bir değer giriniz, örneğin: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "İzin verilen yetkilendirme türlerinin listesi. Her satır için bir değer " "giriniz, örneğin: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Çapraz kaynak istekleri (cross-origin requests) için izin verilen " "kaynakların listesi, her bir satır için tek değer girilir." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Yönlendirme URI'lerinde ve CORS kaynaklarında joker karakterlere (*) izin " "ver. Etkinleştirildiğinde, URI'ler alt alan adlarını eşleştirmek için tek " "bir yıldız içerebilir." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "İzin verilen yanıt türlerinin listesi. Her satır için tek değer giriniz, " "örneğin: code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "istemci" #: idp/oidc/models.py:116 msgid "clients" msgstr "istemciler" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "İki faktörlü kimlik doğrulama ile korunan bir hesaba e-posta adresi " "ekleyemezsiniz." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "İki faktörlü kimlik doğrulamayı devre dışı bırakamazsınız." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "İki faktörlü kimlik doğrulamayı aktif hale getirmeden kurtarma kodu " "oluşturamazsınız." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Doğrulanmış bir e-posta adresiniz olmadığı sürece iki faktörlü kimlik " "doğrulamayı etkinleştiremezsiniz." #: mfa/adapter.py:141 msgid "Master key" msgstr "Ana anahtar" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Yedek anahtar" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "{number} no.lu Anahtar" #: mfa/apps.py:9 msgid "MFA" msgstr "MFA(Çok Faktörlü Kimlik Doğrulama)" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Kurtarma kodları" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP(Zaman Tabanlı Tek Seferlik Parola) Doğrulayıcısı" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Doğrulayıcı kodu" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Şifresiz" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Şifresiz işlemi etkinleştirmek sadece bu anahtarı kullanarak giriş yapmanıza " "olanak sağlar fakat biyometri veya PIN koruması gibi ek gereksinimleri " "zorunlu kılar." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Bu e-posta adresi ile kayıtlı bir hesap bulunmaktadır. Lütfen önce bu hesaba " "giriş yapıp daha sonra %s adlı hesabınızı bağlayınız." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Geçersiz jeton." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Hesabınızda belirlenmiş herhangi bir şifre bulunmamaktadır." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Hesabınızın doğrulanmış e-posta adresi yok." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Kalan son üçüncü taraf hesabınızın bağlantısını kesemezsiniz." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Bu üçüncü taraf hesabı halihazırda farklı bir hesaba bağlıdır." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Sosyal Hesaplar" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "sağlayıcı" #: socialaccount/models.py:53 msgid "provider ID" msgstr "tedarikçi kimlik numarası" #: socialaccount/models.py:57 msgid "name" msgstr "isim" #: socialaccount/models.py:59 msgid "client id" msgstr "istemci kimlik numarası" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "Uygulama kimlik numarası veya tüketici anahtarı" #: socialaccount/models.py:64 msgid "secret key" msgstr "gizli anahtar" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API anahtarı, istemci anahtarı veya tüketici anahtarı" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Anahtar" #: socialaccount/models.py:82 msgid "social application" msgstr "sosyal medya uygulaması" #: socialaccount/models.py:83 msgid "social applications" msgstr "sosyal medya uygulamaları" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "son giriş" #: socialaccount/models.py:121 msgid "date joined" msgstr "katıldığı tarih" #: socialaccount/models.py:122 msgid "extra data" msgstr "ek veri" #: socialaccount/models.py:126 msgid "social account" msgstr "sosyal hesap" #: socialaccount/models.py:127 msgid "social accounts" msgstr "sosyal hesaplar" #: socialaccount/models.py:161 msgid "token" msgstr "jeton" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) veya access token (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "jeton anahtarı" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) veya refresh token (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "geçersiz olma tarihi" #: socialaccount/models.py:175 msgid "social application token" msgstr "sosyal medya uygulaması jetonu" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "sosyal medya uygulaması jetonları" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Geçersiz profil bilgisi" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Giriş Yap" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Iptal" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "\"%s\"'dan istek jetonu temin edilirken geçersiz yanıt alındı. Yanıt %s idi." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "\"%s\"'dan erişim kodu alınırken geçersiz cevap alındı." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "\"%s\" için hiçbir talep kodu kaydedilmedi." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "\"%s\" için hiçbir erişim kodu kaydedilmedi." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "\"%s\"'daki özel kaynaklara erişim yok." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "\"%s\"'dan talep kodu alınırken geçersiz cevap alındı." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Hesap Etkin Değil" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Bu hesap etkin değil." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "%(recipient)s kullanıcısına bir kod gönderdik. Gönderilen kodun süresi kısa " "zamanda dolacağından dolayı lütfen hızlı davranınız." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Onayla" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Yeni kod iste" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Erişim Doğrulama" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Hesabınızı güvenilir hale getirmek için lütfen tekrar doğrulayın." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Alternatif seçenekler" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "E-Posta Onayı" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "E-Posta Onay Kodu Gir" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Farklı bir e-posta adresi kullanınız" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Giriş Yap" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Giriş Kodunu Gir" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Şifre Sıfırlama" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Şifre Sıfırlama Kodu Giriniz" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Telefon Numarası Doğrulama" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Telefon Numarası Onay Kodu Gir" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Farklı bir telefon numarası kullanınız" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "E-posta Adresleri" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Şu e-posta adresleri hesabınızla ilişkilendirildi:" #: templates/account/email.html:25 msgid "Verified" msgstr "Doğrulanmış" #: templates/account/email.html:29 msgid "Unverified" msgstr "Doğrulanmamış" #: templates/account/email.html:34 msgid "Primary" msgstr "Birincil" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Birincil Yap" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Tekrar Doğrula" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Kaldır" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "E-posta Adresi Ekle" #: templates/account/email.html:70 msgid "Add Email" msgstr "E-posta Ekle" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Seçilen e-posta adresini kaldırmak istediğinizden emin misiniz?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Bu e-postayı, siz veya başka birisi aşağıdaki e-posta adresini kullanarak " "kayıt olmaya\n" "çalıştığı için alıyorsunuz:\n" "\n" "%(email)s\n" "\n" "Fakat, bu e-posta adresini kullanan bir hesap halihazırda mevcut " "durumdadır. Eğer unuttuysanız\n" "hesabınızı kurtarmak için lütfen şifre yenileme işleminden\n" "faydalanınız:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Hesap Halihazırda Mevcut" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "%(site_name)s'ten Merhaba!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "%(site_name)s'i kullandığınız için teşekkür ederiz!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "Bu e-postayı, hesabınızda bir değişiklik yapıldığı için alıyorsunuz:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Bu değişiklikten haberdar değilseniz lütfen gerekli güvenlik önlemlerini " "derhal alınız. Hesabınızdaki değişikliğin kaynağı alt kısımda " "belirtilmiştir:\n" "\n" "- IP adresi: %(ip)s\n" "- Tarayıcı: %(user_agent)s\n" "- Tarih: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "" "%(from_email)s adlı e-posta adresiniz %(to_email)s şeklinde değiştirilmiştir." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "E-posta Adresi Değiştirildi" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "E-posta adresiniz onaylandı." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "E-Posta Adresi Onayı" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Bu e-postayı, %(user_display)s adlı kullanıcı, e-posta adresinizi " "%(site_domain)s üzerinde bir hesap oluşturmak adına kullandığı için " "alıyorsunuz." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "E-posta onay kodunuz alt kısımda belirtilmiştir. Kodu, açık olan tarayıcı " "sekmenize giriniz." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "" "Bunun doğru olduğunu düşünüyorsanız %(activate_url)s adlı bağlantıyı ziyaret " "ediniz." #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Lütfen E-posta Adresinizi Onaylayın" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "%(deleted_email)s adlı e-posta adresi hesabınızdan kaldırılmıştır." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "E-Posta Adresi Kaldırıldı" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Giriş kodunuz alt kısımda belirtilmiştir. Kodu, açık olan tarayıcı sekmenize " "giriniz." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Alınan aksiyondan haberdar değilseniz bu e-postayı görmezden gelebilirsiniz." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Giriş Kodu" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Şifreniz değiştirildi." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Şifre Değiştirildi" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Şifre sıfırlama kodunuz alt kısımda belirtilmiştir. Kodu, açık olan tarayıcı " "sekmenize giriniz." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Şifre Sıfırlama Kodu" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Bu e-postayı, siz veya başka biri, kullanıcı hesabınız için şifre sıfırlama " "talebinde bulunduğu için alıyorsunuz.\n" "Eğer şifre sıfırlama talebinde bulunan kişi siz değilseniz bu uyarıyı " "görmezden gelebilirsiniz. Şifrenizi sıfırlamak için aşağıdaki bağlantıya " "tıklayın." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Kullanıcı adınızı unuttuysanız, kullanıcı adınız: %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Şifre Sıfırlama E-postası" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Şifreniz sıfırlandı." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Şifreniz belirlendi." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Şifre Tanımlandı" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Bu e-postayı, siz ya da başka biri %(email)s adlı e-posta adresini " "kullanarak bir hesaba giriş yapmaya çalıştığı için alıyorsunuz. Ancak, giriş " "yapmaya çalışılan hesaba dair veritabanımızda herhangi bir kayıt " "bulunmamaktadır." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Eğer o kişi siz iseniz aşağıdaki linki kullanarak bir hesap " "oluşturabilirsiniz." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Bilinmeyen Hesap" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "E-posta Adresi" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Mevcut e-posta" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Şuna değiştiriliyor" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "E-posta adresiniz hala onay aşamasındadır." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Değişikliği İptal Et" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Şuna değiştir" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "E-posta Adresini Değiştir" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "E-posta Adresi Doğrula" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Lütfen %(email)s adlı e-posta adresinin " "%(user_display)s kullanıcısına ait olduğunu onaylayın." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "%(email)s adlı e-posta adresi başka bir hesap tarafından doğrulandığı için " "onaylanamıyor." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Bu e-posta adresi onaylama bağlantısı sona ermiş veya geçersizdir. Lütfen yeni bir e-posta adresi doğrulama talebinde bulunun.." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Henüz bir hesap oluşturmadıysanız lütfen önce %(link)süye olun%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Geçiş anahtarı ile giriş yap" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Bana giriş kodu gönder" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Çıkış Yap" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Çıkış yapmak istediğinize emin misiniz?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "(%(email)s) adlı birincil e-posta adresinizi kaldıramazsınız." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Doğrulama e-posta'sı %(email)s adresine gönderildi." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "%(email)s adresini onayladınız." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "%(email)s adresini sildiniz." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "%(name)s olarak başarıyla giriş yapıldı." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Çıkış yaptınız." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "%(recipient)s kullanıcısına bir giriş kodu gönderildi." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Şifre başarıyla değiştirildi." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Şifre başarıyla belirlendi." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "%(phone)s nolu numaraya bir giriş kodu gönderildi." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "%(phone)s nolu telefon numaranızı onayladınız." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Birincil e-posta adresi tanımlandı." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Şifre Değiştir" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Şifrenizi mi unuttunuz?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Şifrenizi mi unuttunuz? E-posta adresinizi aşağıya yazın ve size parolanızı " "sıfırlamanıza imkan veren bir e-posta gönderelim." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Şifremi Sıfırla" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Şifrenizi sıfırlarken herhangi bir sorunla karşılaşırsanız lütfen bize " "ulaşın." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Size bir e-posta gönderdik. E-postayı almadıysanız istenmeyen e-posta " "klasörünüzü kontrol ediniz. Eğer birkaç dakika içinde elinize ulaşmaz ise " "bizimle irtibata geçiniz." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Hatalı Anahtar" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Muhtemelen daha önce kullanıldığı için şifre sıfırlama bağlantısı geçersiz. " "Lütfen yeni şifre sıfırlama talebinde " "bulunun." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Şifreniz değiştirildi." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Şifre Belirle" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Telefon Numarası Değiştir" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Geçerli Telefon Numarası" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Telefon numaranız hala onay aşamasındadır." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Telefon Numarası Değiştir" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Şifrenizi giriniz:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Şifresiz giriş yapabilmenizi sağlayan bir kod alacaksınız." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "İstek Kodu" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Diğer giriş seçenekleri" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Üye Ol" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Üye Ol" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "" "Zaten hesabınız var mı? O zaman lütfen %(link)sgiriş yapın%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Geçiş anahtarı kullanarak giriş yap" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Geçiş Anahtarı ile Üye Ol" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Diğer seçenekler" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Kayıt Kapalı" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Üzgünüz, ancak kayıt şu anda kapalıdır." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Not" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Halihazırda %(user_display)s olarak giriş yapmış durumdasınız." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Uyarı:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Şu anda tanımlı hiçbir e-posta adresiniz yok. Bildirim alabilmek, şifrenizi " "sıfırlayabilmek ve benzeri eylemler için e-posta adresinizi mutlaka " "eklemelisiniz." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "E-posta Adresinizi Doğrulayın" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Size, doğrulama amaçlı bir e-posta gönderdik. Kayıt olma aşamasını " "tamamlamak için bağlantıyı ziyaret ediniz. Eğer doğrulama e-postasını gelen " "kutunuzda göremiyorsanız istenmeyen e-posta klasörünüzü kontrol ediniz. Eğer " "e-posta birkaç dakika içinde tarafınıza ulaşmaz ise bizimle irtibata geçiniz." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Sitenin bu kısmı kim olduğunuzu doğrulamanızı\n" "gerektirmektedir. Bu sebeple, girdiğiniz e-posta adresinize sahip\n" "olduğunuzu doğrulamanızı rica ediyoruz. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Doğrulama için size bir e-posta gönderdik.\n" "Lütfen e-postadaki bağlantıyı ziyaret edin. Eğer e-postayı gelen kutunuzda " "göremiyorsanız istenmeyen e-posta klasörünüzü kontrol edin. Eğer birkaç " "dakika\n" "içinde bu e-postayı almazsanız bize ulaşın." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Not: hala e-posta adresinizi " "değiştirebilirsiniz." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Mesajlar:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menü:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Hesap Bağlantıları" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "İki Faktörlü Kimlik Doğrulama" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Oturumlar" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Yetkilendir" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "" "%(client_name)s isimli istemci, %(site_name)s isimli hesabınıza erişim talep " "etmektedir." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Cihaz Kodu Gir" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Cihazınızda gözüken kodu giriniz." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "İlerle" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Cihazı Onayla" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Lütfen bu cihazı yetkilendirmek için %(client_name)s isimli istemcinizde " "gözüken kodu onaylayınız." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Reddet" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Cihaz Yetkilendirildi" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "%(client_name)s isimli cihazınızı başarıyla yetkilendirdiniz." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Cihaz Reddedildi" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "" "%(client_name)s isimli cihazınız için istenilen yetkilendirme reddedildi." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Hata" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Oturumu Açık Tut" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Hesabınız iki faktörlü kimlik doğrulama ile korunmaktadır. Lütfen bir " "doğrulama kodu giriniz:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "Yeni iki faktörlü kimlik doğrulama kurtarma kodu dizesi oluşturuldu." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Yeni Kurtarma Kodları Oluşturuldu" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Kimlik doğrulama uygulaması etkinleştirildi." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Kimlik Doğrulama Uygulaması Etkinleştirildi" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Kimlik doğrulama uygulaması devre dışı bırakıldı." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Kimlik Doğrulama Uygulaması Devre Dışı Bırakıldı" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Yeni bir güvenlik anahtarı eklendi." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Güvenlik Anahtarı Eklendi" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Bir güvenlik anahtarı kaldırıldı." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Güvenlik Anahtarı Kaldırıldı" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Kimlik Doğrulama Uygulaması" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Kimlik doğrulama uygulaması aracılığı ile kimlik doğrulama etkin." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Bir kimlik doğrulama uygulaması etkin değil." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Devre Dışı Bırak" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Etkinleştir" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Güvenlik Anahtarları" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "%(count)s adet güvenlik anahtarı eklediniz." msgstr[1] "%(count)s adet güvenlik anahtarı eklediniz." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Hiçbir güvenlik anahtarı eklenmedi." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Yönet" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Ekle" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Kurtarma Kodları" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "" "%(total_count)s adet kurtarma kodu içerisinden %(unused_count)s sayıda " "kurtarma kodu uygun durumda." msgstr[1] "" "%(total_count)s adet kurtarma kodu içerisinden %(unused_count)s sayıda " "kurtarma kodu uygun durumda." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Tanımlı kurtarma kodu bulunmamakta." #: templates/mfa/index.html:96 msgid "View" msgstr "İncele" #: templates/mfa/index.html:102 msgid "Download" msgstr "İndir" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Oluştur" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Yeni bir kurtarma kodu dizesi oluşturuldu." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Güvenlik anahtarı eklendi." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Güvenlik anahtarı kaldırıldı." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Bir kimlik doğrulama kodu gir:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Hesabınız için yeni bir kurtarma kodu dizesi oluşturmak üzeresiniz." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Bu eylem, halihazırda mevcut olan kodlarınızı geçersiz kılacaktır." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Emin misin?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Kullanılmamış kodlar" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Kodları indir" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Yeni kod oluştur" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Kimlik Doğrulama Uygulaması Etkinleştir" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Hesabınızı iki faktörlü kimlik doğrulama ile korumak adına aşağıdaki QR " "kodunu kimlik doğrulama uygulamanız ile tarayınız. Sonrasında uygulama " "tarafından oluşturulan doğrulama kodunu aşağıdaki kısma giriniz." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Kimlik doğrulama anahtarı" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Bu anahtarı saklayabilir ve kimlik doğrulama uygulamanızı ileriki bir " "zamanda yeniden yüklemek için kullanabilirsiniz." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Kimlik Doğrulama Uygulamasını Devre Dışı Bırak" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Kimlik doğrulama uygulaması tabanlı kimlik doğrulamayı devre dışı bırakmak " "üzeresiniz. Bunu yapmak istediğinizden emin misiniz?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Bu tarayıcıya güveniyor musun?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Bu tarayıcıya güvenmeyi seçerseniz bir sonraki girişinizde onay kodu " "gerekmeyecektir." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "%(period)s için güven" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Güvenme" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Güvenlik Anahtarı Ekle" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Güvenlik Anahtarı Kaldır" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Bu güvenlik anahtarını kaldırmak istediğinizden emin misiniz?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Kullanım" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Geçiş Anahtarı" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Güvenlik anahtarı" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Bu anahtar, bir geçiş anahtarı olduğunu belirtmiyor." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Belirtilmemiş" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "%(created_at)s tarihinde eklendi" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "En son %(last_used)s tarihinde kullanıldı" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Düzenle" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Güvenlik Anahtarını Düzenle" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Kaydet" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Geçiş Anahtarı Oluştur" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Hesabınız için geçiş anahtarı oluşturmak üzeresiniz. İleriki zamanlarda ek " "anahtarlar ekleyebileceğiniz gibi anahtarları ayırt etmek adına açıklayıcı " "isimler kullanabilirsiniz." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Oluştur" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Bu özellik JavaScript gerektirir." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Üçüncü Taraf Giriş Hatası" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "Üçüncü taraf hesabınız ile giriş yapılırken bir hata meydana geldi." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Hesabınıza aşağıdaki üçüncü taraf hesaplar aracılığı ile giriş " "yapabilirsiniz:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Bu hesap ile bağlantılı hiçbir üçüncü taraf hesap bulunmamakta." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Üçüncü Taraf Hesap ekle" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "" "%(provider)s üzerinden bir üçüncü taraf hesap, hesabınız ile " "ilişkilendirildi." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Üçüncü Taraf Hesap İlişkilendirildi" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" "%(provider)s üzerinden üçüncü taraf bir hesabın, hesabınız ile olan " "bağlantısı kesildi." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Üçüncü Taraf Hesap Bağlantısı Kesildi" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "%(provider)s Bağlantısı Kur" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "" "%(provider)s üzerinden yeni bir üçüncü taraf hesap bağlantısı kurmak " "üzeresiniz." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "%(provider)s ile Giriş Yap" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" "%(provider)s üzerinden üçüncü taraf bir hesap ile giriş yapmak üzeresiniz." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Giriş İptal Edildi" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Mevcut hesaplarınızdan birisiyle sitemize girişinizi iptal etmeye karar " "verdiniz. Eğer bu yanlışlıkla olduysa, lütfen devam edin: giriş yapın." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Üçüncü taraf hesap bağlantısı kuruldu." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Üçüncü taraf hesap bağlantısı kesildi." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "%(provider_name)s hesabınızı kullanarak %(site_name)s sitesine giriş yapmak " "üzeresiniz.\n" "Son aşama olarak lütfen formu doldurunuz:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Veya üçüncü taraf bir uygulama kullan" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Diğer tüm oturumlardan çıkış yapıldı." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Şu Tarihte Başladı" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP Adresi" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Tarayıcı" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "En son şu tarihte görüldü" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Şu anki" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Diğer Oturumlardan Çıkış Yap" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Kullanıcı Oturumları" #: usersessions/models.py:94 msgid "session key" msgstr "oturum anahtarı" #~ msgid "Account Connection" #~ msgstr "Hesap Bağlantısı" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "Parola en az {0} karakter olmalıdır." #, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "Bu e-postayı alıyorsunuz çünkü siz veya başka biri kullanıcı hesabınız " #~ "için\n" #~ "bir şifre talebinde bulundu. Ancak,veritabanımızda %(email)s e-posta " #~ "adresi ile ilgili bir kayıt bulunmamaktadır.\n" #~ "\n" #~ "Eğer şifre sıfırlama talebinde bulunmadıysanız bu e-posta güvenle göz " #~ "ardı edilebilir.\n" #~ "\n" #~ "Eğer talep sizden geldiyse, aşağıdaki bağlantıyı kullanarak bir hesap " #~ "oluşturabilirsiniz." #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "Şu e-posta adresleri hesabınızla ilişkilendirildi:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "E-posta Adresi Doğrula" #~ msgid "or" #~ msgstr "ya da" #~ msgid "change password" #~ msgstr "parola değiştir" #~ msgid "OpenID Sign In" #~ msgstr "OpenID Girişi" #~ msgid "This email address is already associated with another account." #~ msgstr "Bu e-post adresi başka bir hesap ile ilişkilendirilmiş." #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "Size bir e-posta gönderdik. Birkaç dakika içerisinde size ulaşmazsa " #~ "lütfen bize ulaşın." #~ msgid "The login and/or password you specified are not correct." #~ msgstr "Girdiğiniz giriş bilgisi ve/veya parola doğru değil." #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "Kullanıcı adları sadece harf, rakam ve @/./+/-/_ içerebilir." #~ msgid "This username is already taken. Please choose another." #~ msgstr "Bu kullanıcı adı alınmış durumda. Lütfen başka bir tane deneyiniz." #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "%(email)s adresinin %(user_display)s " #~ "kullanıcısına ait olduğunu onayladınız." #~ msgid "Thanks for using our site!" #~ msgstr "Sitemizi kullandığınız için teşekkür ederiz!" #~ msgid "Confirmation email sent to %(email)s" #~ msgstr "Onay e-postası %(email)s adresine gönderildi." #~ msgid "Delete Password" #~ msgstr "Parola Sil" #~ msgid "" #~ "You may delete your password since you are currently logged in using " #~ "OpenID." #~ msgstr "" #~ "Şu anda OpenID ile giriş yaptığınız için dilerseniz parolanızı " #~ "silebilirsiniz." #~ msgid "delete my password" #~ msgstr "parolamı sil" #~ msgid "Password Deleted" #~ msgstr "Parola Silindi" ================================================ FILE: allauth/locale/uk/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-09-24 20:02+0000\n" "Last-Translator: Максим Горпиніч \n" "Language-Team: Ukrainian \n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != " "11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % " "100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || " "(n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n" "X-Generator: Weblate 5.14-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Цей обліковий запис наразі неактивний." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Ви не можете видалити свою основну адресу електронної пошти." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Ця адреса електронної пошти вже пов'язана з цим обліковим записом." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Вказана адреса електронної пошти та/або пароль неправильні." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Вказано невірний номер телефону або пароль." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Користувач з цією адресою електронної пошти вже зареєстрований." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Будь ласка, введіть свій поточний пароль." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Неправильний код." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Неправильний пароль." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Недійсний або прострочений ключ." #: account/adapter.py:79 msgid "Invalid login." msgstr "Недійсне назвище." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Токен відновлення пароля недійсний." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Ви не можете додати більше ніж %d адрес електронної пошти." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Користувач з таким тел. номером вже зареєстрований." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "Занадто багато невдалих спроб входу. Спробуйте ще раз пізніше." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "" "Ця адреса електронної пошти не призначена для жодного облікового запису." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Цей номер телефону не призначено для жодного облікового запису." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Ваша основна адреса електронної пошти повинна бути підтверджена." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "" "Ім'я користувача не може бути використане. Будь ласка, виберіть інше ім'я " "користувача." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Вказане ім'я користувача або пароль неправильні." #: account/adapter.py:98 msgid "Please select only one." msgstr "Будь ласка, оберіть лише одне." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Нове значення має відрізнятись від поточного." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Будьте терплячі, ви надсилаєте забагато запитів." #: account/adapter.py:826 msgid "Use your password" msgstr "Використовуйте свій пароль" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Використовуйте додаток для аутентифікації або код" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Використовуйте ключ безпеки" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "Позначено {email} як перевірений." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "Не вдалося позначити {email} як перевірений." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Позначте обрані адреси електронної пошти як підтверджені" #: account/apps.py:11 msgid "Accounts" msgstr "Облікові записи" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "Електронна пошта" #: account/fields.py:19 msgid "Email address" msgstr "Адреса електронної пошти" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" "Введіть номер телефону, включаючи код країни (наприклад +38 для України)." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Номер телефону" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Вам потрібно вводити один і той самий пароль кожен раз." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Пароль" #: account/forms.py:67 msgid "Remember Me" msgstr "Запам'ятати Мене" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Ім'я користувача" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Вхід" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Ім'я користувача, електронна пошта або номер телефону" #: account/forms.py:117 msgid "Username or email" msgstr "Ім'я користувача або електронна пошта" #: account/forms.py:119 msgid "Username or phone" msgstr "Ім'я користувача або номер телефону" #: account/forms.py:121 msgid "Email or phone" msgstr "Електронна пошта або номер телефону" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Забули пароль?" #: account/forms.py:287 msgid "Email (again)" msgstr "Електронна пошта (ще раз)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Підтвердження адреси електронної пошти" #: account/forms.py:302 msgid "Email (optional)" msgstr "Електронна пошта (необов'язково)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Ім'я користувача (необов'язково)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Ви повинні вводити одну й ту ж електронну пошту кожен раз." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Пароль (ще раз)" #: account/forms.py:591 msgid "Current Password" msgstr "Поточний Пароль" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Новий Пароль" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Новий Пароль (ще раз)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Код" #: account/models.py:23 msgid "user" msgstr "користувач" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "адреса електронної пошти" #: account/models.py:31 msgid "verified" msgstr "підтверджено" #: account/models.py:32 msgid "primary" msgstr "основна" #: account/models.py:38 msgid "email addresses" msgstr "адреси електронної пошти" #: account/models.py:142 msgid "created" msgstr "створено" #: account/models.py:143 msgid "sent" msgstr "відправлено" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "ключ" #: account/models.py:149 msgid "email confirmation" msgstr "підтвердження електронної пошти" #: account/models.py:150 msgid "email confirmations" msgstr "підтвердження електронної пошти" #: headless/apps.py:7 msgid "Headless" msgstr "Без візуального інтерфейсу" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Переглянути ваш ідентифікатор користувача" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Переглянути свою адресу електронної пошти" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Переглянути базову інформацію профілю" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Надати дозволи" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "Символи підстановки не дозволені, якщо не увімкнено опцію 'Дозволити символи " "підстановки в URI'." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' містить більше одного символу підстановки (*). Дозволено лише один " "символ підстановки на URI." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Символи підстановки дозволені лише в частині імені хоста URI." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Код авторизації" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Код пристрою" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Облікові дані клієнта" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Токен оновлення" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Конфіденційний" #: idp/oidc/models.py:44 msgid "Public" msgstr "Публічний" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Область(и), яку клієнт може запитувати. Вказуйте одне значення на рядок, " "наприклад: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Якщо клієнт не вказує жодної області дії, використовуються ці області дії за " "замовчуванням. Вказуйте одне значення в кожному рядку, наприклад: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Список дозволених типів грантів. Вказуйте одне значення на рядок, наприклад: " "authorication_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Список дозволених origin-адрес для кросдоменних запитів, по одній на рядок." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Дозволити символи підстановки (*) у URI перенаправлення та джерелах CORS. " "Коли увімкнено, URI можуть містити одну зірочку для відповідності піддоменам." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Список дозволених типів відповідей. Вказуйте одне значення на рядок, " "наприклад: code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "клієнт" #: idp/oidc/models.py:116 msgid "clients" msgstr "клієнти" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Ви не можете додати адресу електронної пошти до облікового запису, який " "захищений двофакторною аутентифікацією." #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Ви не можете вимкнути двофакторну аутентифікацію." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Ви не можете створити коди відновлення без увімкненої двофакторної " "аутентифікації." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Ви не можете увімкнути двофакторну аутентифікацію, поки не підтвердите свою " "адресу електронної пошти." #: mfa/adapter.py:141 msgid "Master key" msgstr "Головний ключ" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Резервний ключ" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Ключ № {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "Багатофакторна аутентифікація" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Коди для відновлення" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "Аутентифікатор TOTP" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Код аутентифікатора" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Без пароля" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Увімкнення режиму без пароля дозволяє вам увійти, використовуючи лише цей " "ключ, але передбачає додаткові вимоги, такі як біометричні дані або захист " "PIN-кодом." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Обліковий запис вже існує з цією адресою електронної пошти. Будь ласка, " "спочатку увійдіть в цей обліковий запис, а потім підключіть ваш обліковий " "запис %s." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Недійсний токен." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "У вашому обліковому записі не встановлено пароль." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "" "У вашому обліковому записі не підтверджено жодної адреси електронної пошти." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "" "Ви не можете відключити ваш останній обліковий запис стороннього " "постачальника." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "" "Обліковий запис стороннього постачальника вже підключений до іншого " "облікового запису." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Облікові Записи Соціальних Мереж" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "постачальник" #: socialaccount/models.py:53 msgid "provider ID" msgstr "ідентифікатор постачальника" #: socialaccount/models.py:57 msgid "name" msgstr "ім'я" #: socialaccount/models.py:59 msgid "client id" msgstr "ідентифікатор клієнта" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "Ідентифікатор додатка або ключ споживача" #: socialaccount/models.py:64 msgid "secret key" msgstr "ключ" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API ключ, клієнтський ключ або ключ споживача" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Ключ" #: socialaccount/models.py:82 msgid "social application" msgstr "додаток соціальної мережі" #: socialaccount/models.py:83 msgid "social applications" msgstr "додатки соціальної мережі" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "останній вхід" #: socialaccount/models.py:121 msgid "date joined" msgstr "дата приєднання" #: socialaccount/models.py:122 msgid "extra data" msgstr "додаткові дані" #: socialaccount/models.py:126 msgid "social account" msgstr "обліковий запис соціальної мережі" #: socialaccount/models.py:127 msgid "social accounts" msgstr "облікові записи соціальних мереж" #: socialaccount/models.py:161 msgid "token" msgstr "токен" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) або токен доступу (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "ключ токена" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) або токен оновлення (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "діє до" #: socialaccount/models.py:175 msgid "social application token" msgstr "токен додатку соціальної мережі" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "токени додатків соціальної мережі" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Недійсні дані профілю" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Увійти" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Скасувати" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "" "Недійсна відповідь при отриманні токена запиту від \"%s\". Відповідь була: " "%s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "Недійсна відповідь під час отримання токена доступу від \"%s\"." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "Не збережено токен запиту для \"%s\"." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "Не збережено токен доступу для \"%s\"." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "Немає доступу до приватних ресурсів за \"%s\"." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "Недійсна відповідь при отриманні токена запиту від \"%s\"." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Обліковий Запис Неактивний" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Цей обліковий запис неактивний." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Ми відправили код на %(recipient)s. Код скоро застаріє, тому будь ласка, " "введіть його якнайшвидше." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Підтвердити" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Запросити новий код" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Підтвердіть Доступ" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "" "Будь ласка, проведіть повторну аутентифікацію для захисту вашого облікового " "запису." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Альтернативні варіанти" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Підтвердження електронної пошти" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Введіть код підтвердження електронної пошти" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Використайте іншу адресу ел. пошти" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Увійти" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Введіть Код Для Входу" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Відновлення Пароля" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Введіть код відновлення паролю" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Підтвердження номеру телефона" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Введіть код підтвердження номеру телефона" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Використайте інший тел. номер" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Адреси електронної пошти" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Ваш обліковий запис пов'язано з наступними адресами електронної пошти:" #: templates/account/email.html:25 msgid "Verified" msgstr "Підтверджено" #: templates/account/email.html:29 msgid "Unverified" msgstr "Непідтверджено" #: templates/account/email.html:34 msgid "Primary" msgstr "Основний" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Зробити Основним" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Надіслати Повторне Підтвердження" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "Видалити" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Додати Адресу Електронної Пошти" #: templates/account/email.html:70 msgid "Add Email" msgstr "Додати Електронну Пошту" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Ви дійсно хочете видалити обрану адресу електронної пошти?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Ви отримали цей лист, оскільки ви або хтось інший спробував зареєструвати\n" "обліковий запис, використовуючи адресу електронної пошти:\n" "\n" "%(email)s\n" "\n" "Однак обліковий запис з цією адресою електронної пошти вже існує. Якщо ви\n" "про це забули, скористайтеся процедурою відновлення пароля, щоб відновити\n" "свій обліковий запис:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Обліковий Запис Вже Існує" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "Привіт від %(site_name)s!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "Дякуємо за використання %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Ви отримуєте цей лист, оскільки була зроблена наступна зміна у вашому " "обліковому записі:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Якщо ви не впізнаєте цю зміну, будь ласка, негайно прийміть відповідні " "заходи безпеки. Зміна у вашому обліковому записі походить з:\n" "\n" "- IP-адреса: %(ip)s\n" "- Браузер: %(user_agent)s\n" "- Дата: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Ваша електронна пошта була змінена з %(from_email)s на %(to_email)s." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Електронна Пошта Змінена" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Ваша електронна пошта підтверджена." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Підтвердження Електронної Пошти" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Ви отримали цей лист, оскільки користувач %(user_display)s вказав вашу " "електронну пошту для реєстрації облікового запису на %(site_domain)s." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Нижче наведено код підтвердження електронної пошти. Введіть його у " "відкритому вікні браузера." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "" "Для підтвердження реєстрації перейдіть за наступним посиланням: " "%(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Будь Ласка, Підтвердіть Вашу Адресу Електронної Пошти" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "" "Адресу електронної пошти %(deleted_email)s було видалено з вашого облікового " "запису." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Електронна Пошта Видалена" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Нижче наведений код для входу. Будь ласка, введіть його у відкритому вікні " "браузера." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "Цей лист можна безпечно ігнорувати, якщо ви не ініціювали цю дію." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Код Для Входу" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Ваш пароль було змінено." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Пароль Змінено" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Нижче наведений код відновлення паролю. Введіть його у відкритому вікні " "браузера." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Код відновлення паролю" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Ви отримали цей лист, оскільки ви або хтось інший запросив зміну пароля для " "вашого облікового запису.\n" "Якщо ви не запитували зміну пароля, можете ігнорувати цей лист. Натисніть на " "посилання нижче, щоб змінити пароль." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Якщо ви забули, ваше ім'я користувача - %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Лист Для Відновлення Пароля" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Ваш пароль було відновленно." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Ваш пароль було встановлено." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Встановити Пароль" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Ви отримали цей лист, оскільки ви або хтось інший спробували отримати доступ " "до облікового запису з електронною поштою %(email)s. Проте у нашій базі " "даних немає жодного запису такого облікового запису." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Якщо це були ви, ви можете зареєструватися для облікового запису за " "допомогою посилання нижче." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Невідомий Обліковий Запис" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Адреса Електронної Пошти" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Поточна електронна пошта" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "Зміна на" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "Ваша адреса електронної пошти ще не підтверджена." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "Відмінити Зміну" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "Зміна на" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Змінити Електронну Пошту" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Підтвердити Адресу Електронної Пошти" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Будь ласка, підтвердіть, що %(email)s є " "адресоюелектронної пошти для користувача %(user_display)s." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "Не вдалося підтвердити %(email)s, оскільки вона вже підтверджена іншим " "обліковим записом." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Термін дії посилання для підтвердження електронної пошти закінчився або воно " "є недійсним. Будь ласка, відправте новий запит на " "підтвердження електронної пошти." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Якщо ви ще не створили обліковий запис, будь ласка, спочатку " "%(link)sзареєструйтесь%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "Увійти за допомогою пароль-ключа" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Надішліть мені код для входу" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Вийти" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Ви впевнені, що хочете вийти?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "" "Ви не можете видалити свою основну адресу електронної пошти (%(email)s)." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Лист-підтвердження надіслано на %(email)s." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "Ви підтвердили %(email)s." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "Видалено адресу електронної пошти %(email)s." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "Успішно увійшли як %(name)s." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Ви вийшли." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Код для входу було надіслано на %(recipient)s." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Пароль успішно змінено." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Пароль успішно встановлено." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Код підтвердження надіслано на %(phone)s." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Номер телефону %(phone)s підтверджено." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Основна адреса електронної пошти встановлена." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Змінити Пароль" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Забули Пароль?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Забули пароль? Введіть свою адресу електронної пошти нижче, і ми надішлемо " "вам листа з інструкціями для його відновлення." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Відновити Мій Пароль" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Будь ласка, зв'яжіться з нами, якщо у вас виникли проблеми зі відновлення " "пароля." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Ми відправили вам електронного листа. Якщо ви його не отримали, перевірте " "папку «Спам». В іншому випадку, зверніться до нас, якщо ви не отримаєте його " "протягом кількох хвилин." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Недійсний Токен" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Посилання для відновлення пароля недійсне, можливо, воно вже було " "використане. Будь ласка, запросіть нове " "відновлення пароля." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Ваш пароль було змінено." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Встановити Пароль" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Змінити номер телефону" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Поточний номер телефону" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Ваш номер телефону ще не підтверджено." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Змінити номер телефону" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Введіть ваш пароль:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Ви отримаєте особливий код для входу без пароля." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Запросити Код" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Інші варіанти входу" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Реєстрація" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Зареєструватись" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "" "Вже маєте обліковий запис? Тоді будь ласка, %(link)sувійти%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Зареєструватися за допомогою ключа доступу" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Реєстрація за допомогою ключа доступу" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Інші варіанти" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Реєстрація Закрита" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Вибачте, але реєстрація наразі закрита." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Примітка" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Ви вже увійшли як %(user_display)s." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Попередження:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "На даний момент у вас немає встановленої жодної адреси електронної пошти. " "Рекомендується додати адресу електронної пошти для отримання сповіщень, " "відновлення паролю та інших операцій." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Підтвердіть Вашу Адресу Електронної Пошти" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Ми відправили вам лист для підтвердження. Слідуйте по посиланню, щоб " "завершити процес реєстрації. Якщо ви не знайшли листа в основній скринці, " "перевірте папку «Спам». Будь ласка, зв'яжіться з нами, якщо ви не отримаєте " "листа з підтвердженням протягом кількох хвилин." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Ця частина сайту вимагає підтвердження того, що\n" "ви той, за кого себе видаєте. З цією метою ми потребуємо\n" "підтвердити власність вашої адреси електронної пошти. " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Ми відправили вам лист для\n" "підтвердження. Будь ласка, перейдіть за посиланням у цьому листі. Якщо ви не " "знайшли листа з підтвердженням у головній скринці, перевірте папку «Спам». " "Інакше\n" "зв'яжіться з нами, якщо ви не отримаєте його протягом кількох хвилин." #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Примітка: ви все ще можете змінити свою адресу електронної пошти." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Повідомлення:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Меню:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Підключені Облікові Записи" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Двофакторна аутентифікація" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Сесії" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Авторизувати" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "" "%(client_name)s хоче отримати доступ до вашого облікового запису " "%(site_name)s." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Введіть код пристрою" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Введіть код, відображений на вашому пристрої." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Продовжити" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Підтвердити пристрій" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Будь ласка, підтвердьте код, що відображається на вашому %(client_name)s, " "щоб авторизувати цей пристрій." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Заперечити" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Пристрій Авторизовано" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "Ви успішно авторизували свій пристрій %(client_name)s." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Пристрій відхилено" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "Авторизацію для вашого пристрою %(client_name)s відхилено." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Помилка" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Залишитися в системі" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Ваш обліковий запис захищений двофакторною аутентифікацією. Будь ласка, " "введіть код автентифікатора:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Згенеровано новий набір кодів для відновлення двофакторної аутентифікації." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Створено Нові Коди Для Відновлення Доступу" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Аутентифікаційний додаток увімкнено." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Аутентифікаційний Додаток Увімкнено" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Аутентифікаційний додаток вимкнуто." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Аутентифікаційний Додаток Вимкнуто" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Додано новий ключ безпеки." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Додано Ключ Безпеки" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Ключ безпеки було видалено." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Ключ Безпеки Видалено" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Додаток Для Аутентифікації" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Аутентифікація за допомогою додатка для аутентифікації увімкнена." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Додаток для аутентифікації не увімкнений." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Вимкнути" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Увімкнути" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Ключі Безпеки" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "Ви додали %(count)s захисний ключ." msgstr[1] "Ви додали %(count)s захисних ключів." msgstr[2] "Ви додали %(count)s захисних ключів." msgstr[3] "Ви додали %(count)s захисних ключів." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Не було додано жодних ключів безпеки." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Керувати" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Додати" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Коди Для Відновлення" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "Доступно %(unused_count)s з %(total_count)s код відновлення." msgstr[1] "Доступно %(unused_count)s з %(total_count)s коди відновлення." msgstr[2] "Доступно %(unused_count)s з %(total_count)s кодів відновлення." msgstr[3] "Доступно %(unused_count)s з %(total_count)s кодів відновлення." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Коди для відновлення не налаштовано." #: templates/mfa/index.html:96 msgid "View" msgstr "Перегляд" #: templates/mfa/index.html:102 msgid "Download" msgstr "Завантажити" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Згенерувати" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Згенеровано новий набір кодів для відновлення." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Додано ключ безпеки." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Ключ безпеки видалено." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Введіть код з аутентифікаційного додатка:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "" "Ви збираєтесь згенерувати новий набір кодів для відновлення вашого " "облікового запису." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Ця дія зробить недійсними ваші поточні коди." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Ви впевнені?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Не використані коди" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Завантажити коди" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Згенерувати нові коди" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Увімкнути Додаток Для Аутентифікації" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Для захисту вашого облікового запису двофакторною аутентифікацією " "відскануйте QR-код нижче за допомогою вашого додатка для аутентифікації. " "Потім введіть згенерований додатком код підтвердження нижче." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Ключ аутентифікатора" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Ви можете зберегти цей ключ і використовувати його для перевстановлення " "вашого додатка для аутентифікації в майбутньому." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Вимкнути Додаток Для Аутентифікації" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Ви збираєтесь вимкнути аутентифікацію на основі додатку для аутентифікації. " "Ви впевнені?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Довіряти цьому браузеру?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Якщо оберете довіряти цьому переглядачу, то при наступному вході у вас не " "запитуватимуть код підтвердження входу." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "Довіряти протягом %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Не довіряти" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Додати Ключ Безпеки" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Видалити Ключ Безпеки" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Ви впевнені, що хочете видалити цей ключ безпеки?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Використання" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "пароль-ключ" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Ключ безпеки" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Цей ключ не вказує, чи є він пароль-ключем." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Невизначений" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "Додано %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "Останнє використання %(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "Редагувати" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Редагувати Ключ Безпеки" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Зберегти" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Створити ключ доступу" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Ви створюєте ключ доступу для вашої обліковки. Оскільки можна створити " "більше одного ключа доступу, ви можете обрати назву для кожного з них." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Створити" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Ця функція потребує JavaScript." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Невдалий Вхід Через Сторонній Сервіс" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Під час спроби входу через ваш обліковий запис стороннього сервісу сталася " "помилка." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Ви можете увійти до свого облікового запису, використовуючи будь-який з " "наступних облікових записів сторонніх сервісів:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "" "Наразі до цього облікового запису не підключено жодних облікових записів " "сторонніх сервісів." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Додати Обліковий Запис Стороннього Сервісу" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "" "Обліковий запис від %(provider)s було підключено до вашого облікового запису." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Обліковий Запис Стороннього Сервісу Підключено" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" "Обліковий запис від %(provider)s було відключено від вашого облікового " "запису." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Обліковий Запис Стороннього Сервісу Відключено" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "Підключити %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "" "Ви збираєтеся підключити новий обліковий запис стороннього сервісу " "%(provider)s." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "Увійти через %(provider)s" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" "Ви збираєтеся увійти, використовуючи обліковий запис стороннього сервісу " "%(provider)s." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Вхід Скасовано" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Ви вирішили скасувати вхід на наш сайт, використовуючи один із ваших " "існуючих облікових записів. Якщо це була помилка, будь ласка, перейдіть до " "входу." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Обліковий запис стороннього сервісу було підключено." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Обліковий запис стороннього сервісу було відключено." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Ви збираєтеся увійти за допомогою облікового запису %(provider_name)s на\n" " сайт %(site_name)s. Як завершальний крок, будь ласка, заповніть наступну " "форму:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Або скористайтеся стороннім сервісом" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Усі інші сеанси завершено." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Розпочато о" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP Адреса" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Браузер" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Останній вхід о" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Поточний" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Вийти З Інших Сеансів" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Сеанси Користувача" #: usersessions/models.py:94 msgid "session key" msgstr "ключ сеансу" #, fuzzy #~ msgid "Account Connection" #~ msgstr "Підключені Облікові Записи" ================================================ FILE: allauth/locale/uz/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) 2024 THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Hojiakbar Barotov barotovhojiakbar.me@gmail.com, 2024. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-06-09 11:01+0000\n" "Last-Translator: ONVETI \n" "Language-Team: Uzbek \n" "Language: uz\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 5.12-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "Bu akkaunt hozirda faol emas." #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "Siz asosiy email manzilingizni olib tashlay olmaysiz." #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "Bu email manzili allaqachon ushbu akkaunt bilan bog'langan." #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "Siz ko'rsatgan email manzili va/yoki parol noto'g'ri." #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "Siz ko'rsatgan telefon raqami va/yoki parol noto'g'ri." #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "Foydalanuvchi allaqachon ushbu email manzili bilan ro'yxatdan o'tgan." #: account/adapter.py:75 msgid "Please type your current password." msgstr "Iltimos, joriy parolingizni kiriting." #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "Noto'g'ri kod." #: account/adapter.py:77 msgid "Incorrect password." msgstr "Noto'g'ri parol." #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "Yaroqsiz yoki muddati o'tgan kalit." #: account/adapter.py:79 msgid "Invalid login." msgstr "Noto'g'ri tizimga kirish." #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "Parolni tiklash tokeni yaroqsiz." #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "Siz %d dan ortiq email manzilini qo‘sha olmaysiz." #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "Foydalanuvchi allaqachon ushbu telefon raqami bilan ro'yxatdan o'tgan." #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "" "Kirish uchun juda koʻp muvaffaqiyatsiz urinishlar. Keyinroq qayta urinib " "ko'ring." #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "Email manzili hech qanday foydalanuvchi akkauntiga biriktirilmagan." #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "Telefon raqami hech qanday foydalanuvchi akkauntiga biriktirilmagan." #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "Asosiy email manzilingiz tasdiqlanishi kerak." #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "" "Foydalanuvchi nomidan foydalanib bo'lmaydi. Iltimos, boshqa foydalanuvchi " "nomidan foydalaning." #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "Siz ko'rsatgan foydalanuvchi nomi va/yoki paroli noto'g'ri." #: account/adapter.py:98 msgid "Please select only one." msgstr "Iltimos, faqat bittasini tanlang." #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "Yangi qiymat hozirgi qiymatdan farq qilishi kerak." #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "Sabrli bo'ling, siz judayam koʻp soʻrov yuboryapsiz." #: account/adapter.py:826 msgid "Use your password" msgstr "Parolingizdan foydalaning" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "Autentifikatsiya ilovasi yoki kodidan foydalaning" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "Xavfsizlik kodidan foydalaning" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "{email} tasdiqlangan deb belgilandi." #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "{email} ni tasdiqlangan deb belgilab bo'lmadi." #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "Tanlangan email manzillarini tasdiqlangan deb belgilash" #: account/apps.py:11 msgid "Accounts" msgstr "Hisoblar" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "Email" #: account/fields.py:19 msgid "Email address" msgstr "Email manzili" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "" "Telefon raqamini mamlakat kodi bilan birga kiriting (masalan, UZ uchun " "+998, )." #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "Telefon" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "Har safar bir xil parolni kiritishingiz kerak." #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "Parol" #: account/forms.py:67 msgid "Remember Me" msgstr "Meni Eslab Qolmoq" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "Foydalanuvchi nomi" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "Kirish" #: account/forms.py:115 msgid "Username, email or phone" msgstr "Foydalanuvchi nomi, email yoki telefon" #: account/forms.py:117 msgid "Username or email" msgstr "Foydalanuvchi nomi yoki email" #: account/forms.py:119 msgid "Username or phone" msgstr "Foydalanuvchi nomi yoki telefon" #: account/forms.py:121 msgid "Email or phone" msgstr "Email yoki telefon" #: account/forms.py:144 msgid "Forgot your password?" msgstr "Parolingiz esdan chiqdimi?" #: account/forms.py:287 msgid "Email (again)" msgstr "Email (yana)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "Email manzilini tasdiqlash" #: account/forms.py:302 msgid "Email (optional)" msgstr "Email (ixtiyoriy)" #: account/forms.py:314 msgid "Username (optional)" msgstr "Foydalanuvchi nomi (ixtiyoriy)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "Har safar bir xil email manzilini yozishingiz kerak." #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "Parol (yana)" #: account/forms.py:591 msgid "Current Password" msgstr "Joriy Parol" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "Yangi Parol" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "Yangi Parol (yana)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "Kod" #: account/models.py:23 msgid "user" msgstr "foydalanuvchi" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "email manzili" #: account/models.py:31 msgid "verified" msgstr "tasdiqlangan" #: account/models.py:32 msgid "primary" msgstr "asosiy" #: account/models.py:38 msgid "email addresses" msgstr "email manzillari" #: account/models.py:142 msgid "created" msgstr "yaratilgan" #: account/models.py:143 msgid "sent" msgstr "jo'natilgan" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "kalit" #: account/models.py:149 msgid "email confirmation" msgstr "email orqali tasdiqlash" #: account/models.py:150 msgid "email confirmations" msgstr "email orqali tasdiqlash" #: headless/apps.py:7 msgid "Headless" msgstr "Headless" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "Foydalanuvchi identifikatoringizni ko'rish" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "Email manzilingizni ko'rish" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "Asosiy profil ma'lumotlaringizni ko'rish" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "Ruxsatlarni berish" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "" "'URI joker belgilariga ruxsat berish' yoqilmagan bo'lsa, joker belgilarga " "ruxsat berilmaydi." #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "" "URI '{}' bir nechta joker belgini (*) o'z ichiga oladi. Har bir URI uchun " "faqat bitta joker belgiga ruxsat beriladi." #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "Joker belgilar faqat URI ning xost nomi qismida ruxsat etiladi." #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "Avtorizatsiya kodi" #: idp/oidc/models.py:38 msgid "Device code" msgstr "Qurilma kodi" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "Mijoz hisob ma'lumotlari" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "Yangilash tokeni" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "Maxfiy" #: idp/oidc/models.py:44 msgid "Public" msgstr "Ommaviy" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Mijozga so'rash ruxsat etilgan doira(lar). Har bir qatorga bitta qiymat " "kiriting, masalan: openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "Agar mijoz hech qanday doirani ko'rsatmasa, ushbu standart doiralar " "ishlatiladi. Har bir qatorga bitta qiymat kiriting, masalan: " "openid(ENTER)profile(ENTER)email(ENTER)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "Ruxsat etilgan grant turlari ro'yxati. Har bir qatorga bitta qiymat " "kiriting, masalan: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "" "Cross-origin so'rovlar uchun ruxsat etilgan manbalar ro'yxati, har bir " "qatorda bittadan." #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "Qayta yo'naltirish URI lari va CORS manbalarida joker belgilarga (*) ruxsat " "berish. Yoqilganda, URI lar subdomenlarni moslashtirish uchun bitta " "yulduzcha belgisini o'z ichiga olishi mumkin." #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "Ruxsat etilgan javob turlari ro'yxati. Har bir qatorga bitta qiymat " "kiriting, masalan: code(ENTER)id_token token(ENTER)" #: idp/oidc/models.py:115 msgid "client" msgstr "mijoz" #: idp/oidc/models.py:116 msgid "clients" msgstr "mijozlar" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "" "Siz ikki-bosqichli autentifikatsiya bilan himoyalangan akkauntga email " "manzilini qo'sha olmaysiz" #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "Ikki-bosqichli autentifikatsiyani o'chirib bo'lmaydi." #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "" "Siz ikki-bosqichli autentifikatsiyasiz tiklash kodlarini yarata olmaysiz." #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "" "Siz email manzilingizni tasdiqlamaguningizcha ikki-bosqichli " "autentifikatsiyani faollashtira olmaysiz." #: mfa/adapter.py:141 msgid "Master key" msgstr "Asosiy kalit" #: mfa/adapter.py:143 msgid "Backup key" msgstr "Zaxira kalit" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "Kalit No. {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "Ko'p faktorli autentifikatsiya" #: mfa/models.py:24 msgid "Recovery codes" msgstr "Qayta tiklash kodlari" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP Authenticator" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "Autentifikatsiya kodi" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "Parolsiz" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "Parolsiz operatsiyani yoqish sizga faqat shu kalit yordamida tizimga kirish " "imkonini beradi, lekin biometrik yoki PIN-kod himoyasi kabi qo'shimcha " "talablarni qo'yadi." #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "Ushbu email manzili bilan akkaunt allaqachon mavjud. Iltimos, avval o'sha " "akkauntga kiring keyin %s akkauntingizni ulang." #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "Token yaroqsiz." #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "Akkauntingizda parol sozlanmagan." #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "Akkauntingizda tasdiqlangan email manzili yo'q." #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "Oxirgi qolgan uchinchi tomon akkauntingizni oʻchira olmaysiz." #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "Uchinchi tomon akkaunti allaqachon boshqa akkauntga ulangan." #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "Ijtimoiy Akkauntlar" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "provayder" #: socialaccount/models.py:53 msgid "provider ID" msgstr "provayder ID" #: socialaccount/models.py:57 msgid "name" msgstr "nom" #: socialaccount/models.py:59 msgid "client id" msgstr "mijoz identifikatori" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "Ilova identifikatori yoki iste'molchi kaliti" #: socialaccount/models.py:64 msgid "secret key" msgstr "maxfiy kalit" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API kaliti, mijoz kaliti yoki iste'molchi kaliti" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "Kalit" #: socialaccount/models.py:82 msgid "social application" msgstr "ijtimoiy ilova" #: socialaccount/models.py:83 msgid "social applications" msgstr "ijtimoiy ilovalar" #: socialaccount/models.py:118 msgid "uid" msgstr "uid" #: socialaccount/models.py:120 msgid "last login" msgstr "so'nggi kirish" #: socialaccount/models.py:121 msgid "date joined" msgstr "qo'shilgan sana" #: socialaccount/models.py:122 msgid "extra data" msgstr "qo'shimcha ma'lumotlar" #: socialaccount/models.py:126 msgid "social account" msgstr "ijtimoiy akkaunt" #: socialaccount/models.py:127 msgid "social accounts" msgstr "ijtimoiy akkauntlar" #: socialaccount/models.py:161 msgid "token" msgstr "token" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) yoki kirish tokeni (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "token kaliti" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) yoki yangilash tokeni (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "da tugaydi" #: socialaccount/models.py:175 msgid "social application token" msgstr "ijtimoiy tarmoq tokeni" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "ijtimoiy tarmoq tokenlari" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "Profil maʼlumotlari yaroqsiz" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "Kirish" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "Bekor qilish" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "\"%s\"dan soʻrov tokenini olishda yaroqsiz javob. Olingan javob: %s." #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "\"%s\"dan kirish tokenini olishda yaroqsiz javob." #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "\"%s\" uchun soʻrov tokeni saqlanmagan." #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "\"%s\" uchun kirish tokeni saqlanmagan." #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "\"%s\" shaxsiy ma'lumotlariga kirish imkoni yo'q." #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "\"%s\"dan soʻrov tokenini olishda yaroqsiz javob." #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "Nofaol Akkaunt" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "Bu akkaunt faol emas." #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "" "Biz %(recipient)s manziliga kod yubordik. Kodning amal qilish muddati tez " "orada tugaydi, iltimos tez orada kodni kiriting." #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "Tasdiqlash" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "Yangi kod so'rash" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "Kirishni Tasdiqlash" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "Akkauntingizni himoya qilish uchun qayta autentifikatsiya qiling." #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "Muqobil variantlar" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "Emailni Tasdiqlash" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "Email Tasdiqlash Kodini Kiritish" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "Boshqa email manzilidan foydalanish" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "Kirish" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "Kirish Kodini Kiritish" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "Parolni Tiklash" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "Parolni tiklash kodini kiritish" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "Telefon tasdiqlash" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "Telefon tasdiqlash kodini kiritish" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "Boshqa telefon raqamidan foydalanish" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Email Manzillari" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "Quyidagi email manzillari akkauntingiz bilan bog'langan:" #: templates/account/email.html:25 msgid "Verified" msgstr "Tasdiqlangan" #: templates/account/email.html:29 msgid "Unverified" msgstr "Tasdiqlanmagan" #: templates/account/email.html:34 msgid "Primary" msgstr "Asosiy" #: templates/account/email.html:44 msgid "Make Primary" msgstr "Asosiy Sifatida O'rnatish" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "Tekshirishni Qayta Yuborish" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "O'chirish" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "Email Manzili Qo'shish" #: templates/account/email.html:70 msgid "Add Email" msgstr "Email Qo'shish" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "Haqiqatan ham tanlangan email manzilini o'chirib tashlamoqchimisiz?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "Siz ushbu xatni siz yoki boshqa birov\n" "%(email)s email manzilidan foydalanib akkaunt yaratishga harakat qilgani " "uchun oldingiz.\n" "Biroq, bu email manzilidan foydalanadigan hisob allaqachon mavjud.\n" "Bu haqda unutgan bo'lsangiz, hisobingizni tiklash uchun parolni tiklash " "protsedurasidan foydalaning:\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "Akkaunt Allaqachon Mavjud" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "%(site_name)s dan salom!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "%(site_name)s dan foydalanganingiz uchun tashakkur!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "" "Sizga quyidagi akkauntga o‘zgartirish kiritilgani uchun bu xatni olmoqdasiz" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "Agar siz bu oʻzgarishdan bexabar bo'lsangiz, darhol tegishli xavfsizlik " "choralarini koʻring. Hisobingizga kiritilgan oʻzgarish:\n" "\n" "- IP manzil: %(ip)s\n" "- Brauzer: %(user_agent)s\n" "- Sana: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "Sizning emailingiz %(from_email)s dan %(to_email)s ga o‘zgartirildi." #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Email O'zgartirildi" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "Emailngiz tasdiqlandi." #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Emailni Tasdiqlash" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "Siz bu xatni olmoqdasiz, chunki %(user_display)s foydalanuvchisi " "%(site_domain)s saytida hisob qaydnomasini ro‘yxatdan o‘tkazish uchun email " "manzilingizni bergan." #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "" "Emailngizni tasdiqlash kodi quyida keltirilgan. Iltimos, uni ochiq brauzer " "oynasida kiriting." #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "Bu toʻgʻriligini tasdiqlash uchun %(activate_url)s ga oʻting" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "Iltimos, Email Manzilingizni Tasdiqlang" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "%(deleted_email)s email manzili akkauntingizdan olib tashlandi." #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Email Olib Tashlandi" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "" "Kirish kodingiz quyida keltirilgan. Iltimos, uni ochiq brauzer oynasida " "kiriting." #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "" "Agar siz ushbu harakatni boshlamagan bo'lsangiz, bu xatni e'tiborsiz " "qoldirish mumkin." #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "Kirish Kodi" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "Parolingiz o'zgartirildi." #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "Parol O'zgartirildi" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "" "Parolni tiklash kodingiz quyida keltirilgan. Iltimos, uni ochiq brauzer " "oynasida kiriting." #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "Parolni tiklash kodi" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "Siz yoki boshqa birov foydalanuvchi akkauntingiz uchun parolni tiklashni " "soʻragani uchun bu xatni olmoqdasiz.\n" "Agar siz parolni tiklashni so'ramagan bo'lsangiz, buni e'tiborsiz " "qoldirishingiz mumkin. Parolni tiklash uchun quyidagi havolani bosing." #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "Agar unutgan bo'lsangiz, sizning foydalanuvchi nomingiz %(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "Parolni Tiklash Emaili" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "Parolingiz qayta tiklandi." #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "Parolingiz oʻrnatildi." #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "Parol O'rnatildi" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "Siz yoki boshqa birov %(email)s emaili bilan kirishga harakat qilgani uchun " "ushbu xatni oldingiz Biroq, bizning ma'lumotlar ba'zasida bunday akkaunt " "yo'q." #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "" "Agar bu siz bo'lsangiz, quyidagi havola orqali akkaunt yaratishingiz mumkin." #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "Noma'lum Akkaunt" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Email Manzili" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "Joriy email" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "-ga o'zgartirilyapti" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "email manzilingiz hali tasdiqlanishi kutilmoqda." #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "O'zgartirishni Bekor Qilish" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "-ga o'zgartirmoq" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "Emailni o'zgartirmoq" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "Email Manzilini Tasdiqlash" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "Iltimos, %(email)s email manzili " "%(user_display)s foydalanuvchisi uchun email ekanligini tasdiqlang." #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "" "%(email)s ni tasdiqlab bo‘lmadi, chunki u allaqachon boshqa akkaunt " "tomonidan tasdiqlangan." #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "Ushbu emailni tasdiqlash havolasi muddati tugagan yoki yaroqsiz. Iltimos, yangi email orqali tasdiqlash so'rovini yuboring." #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "" "Agar siz hali akkaunt yaratmagan bo'lsangiz, iltimos, birinchi " "%(link)sakkaunt yarating%(end_link)s." #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "O'tish kaliti bilan tizimga kirish" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "Menga kirish kodini yuboring" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "Chiqish" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "Haqiqatan ham tizimdan chiqmoqchimisiz?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "Siz asosiy email manzilingizni (%(email)s) olib tashlay olmaysiz." #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "Tasdiqlash xati %(email)s manziliga yuborildi." #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "%(email)s ni tasdiqladingiz." #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "%(email)s email manzili olib tashlandi." #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "%(name)s sifatida muvaffaqiyatli kirildi." #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "Tizimdan chiqdingiz." #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "Kirish kodi %(recipient)s manziliga yuborildi." #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "Parol muvaffaqiyatli almashtirildi." #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "Parol muvaffaqiyatli oʻrnatildi." #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "Tasdiqlash kodi %(phone)s raqamiga yuborildi." #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "Siz %(phone)s telefon raqamini tasdiqladingiz." #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "Asosiy email manzili oʻrnatildi." #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "Parolni O'zgartirmoq" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "Parolni Unutidingizmi?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "Parolni unutdingizmi? Quyida email manzilingizni kiriting, biz sizga uni " "qayta o'rnatishga ruxsat beruvchi emailni yuboramiz." #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "Parolimni Tiklamoq" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "" "Agar parolingizni qayta tiklashda muammoga duch kelsangiz, biz bilan " "bog'laning." #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "Biz sizga email xabarini yubordik. Agar uni olmagan bo'lsangiz, iltimos, " "spam jildini tekshiring. Aks holda, agar siz uni bir necha daqiqada olmagan " "bo'lsangiz, biz bilan bog'laning." #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "Yaroqsiz Token" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "Parolni tiklash havolasi yaroqsiz, ehtimol u allaqachon ishlatilgan. " "Iltimos, yangi parolni tiklashni so'rang." #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "Parolingiz o'zgartirildi." #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "Parol O'rnatish" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "Telefonni o'zgartirish" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "Joriy telefon" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "Telefon raqamingiz hali tasdiqlanishi kutilmoqda." #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "Telefonni o'zgartirish" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "Parolingizni kirgizing" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "Siz parolsiz kirish uchun maxsus kodni olasiz." #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "Kod So'rash" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "Boshqa kirish variantlari" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "Ro'yxatdan O'tish" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "Ro'yxatdan O'tish" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "" "Allaqachon akkauntingiz bormi? Unda akkauntingizga " "%(link)skiring%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "Parol kaliti yordamida ro'yxatdan o'tish" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Parol kaliti bilan ro'yxatdan o'tish" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "Boshqa variantlar" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "Ro'yxatdan O'tish Yopiq" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "Kechirasiz, lekin roʻyxatdan oʻtish hozirda yopiq." #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "Eslatma" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "Siz allaqachon %(user_display)s sifatida tizimga kirgansiz." #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "Ogohlantirish:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "Sizda hozirda hech qanday email manzili oʻrnatilmagan. Haqiqatan ham " "bildirishnomalarni qabul qilish, parolni tiklash va h.k. uchun email manzil " "qoʻshishingiz kerak." #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "Email Manzilingizni Tasdiqlash" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "Tasdiqlash uchun sizga xat yubordik. Roʻyxatdan oʻtish jarayonini yakunlash " "uchun taqdim etilgan havolaga oʻting. Agar tasdiqlash xatini asosiy pochta " "qutingizda koʻrmasangiz, spam jildini tekshiring. Agar bir necha daqiqa " "ichida tasdiqlash xatini olmagan boʻlsangiz, biz bilan bogʻlaning." #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "Saytning ushbu qismi bizdan\n" " siz o'zingizni da'vo qilayotgan shaxs ekanligingizni tasdiqlashimizni talab " "qiladi. Buning uchun\n" " email manzilingizga egaligingizni tasdiqlashingizni talab qilamiz." #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "Tasdiqlash uchun\n" " sizga xat yubordik. Iltimos, ushbu xat ichidagi havolani bosing. Agar " "tasdiqlash xatini asosiy pochta qutingizda koʻrmasangiz, spam jildini " "tekshiring. Aks holda bir necha daqiqa ichida uniqabul qilmasangiz,\n" " biz bilan bogʻlaning. " #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "Eslatma: siz hali ham email " "manzilingizni o‘zgartirishingiz mumkin." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "Xabarlar:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "Menyu:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "Ulangan Akkauntlar" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "Ikki-bosqichli Autentifikatsiya" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "Seanslar" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "Avtorizatsiya qilish" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "" "%(client_name)s sizning %(site_name)s akkauntingizga kirish huquqini " "so'ramoqda." #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "Qurilma kodini kiritish" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "Qurilmangizda ko'rsatilgan kodni kiriting." #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "Davom Etish" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "Qurilmani tasdiqlash" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "" "Ushbu qurilmani avtorizatsiya qilish uchun %(client_name)s da ko'rsatilgan " "kodni tasdiqlang." #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "Rad etish" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "Qurilma avtorizatsiya qilindi" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "" "Siz %(client_name)s qurilmangizni muvaffaqiyatli avtorizatsiya qildingiz." #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "Qurilma rad etildi" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "%(client_name)s qurilmangiz uchun avtorizatsiya rad etildi." #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "Xato" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "Tizimda qolish" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "" "Akkauntingiz ikki-bosqichli autentifikatsiya bilan himoyalangan. Iltimos, " "autentifikatsiya kodini kiriting:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "" "Ikki-bosqichli autentifikatsiyani tiklash kodlarining yangi to'plami " "yaratildi." #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "Yangi Tiklash Kodlari Yaratildi" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "Autentifikatsiya ilovasi faollashtirildi." #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "Autentifikatsiya Ilovasi Faollashtirildi" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "Authenticator ilovasi faolsizlantirildi." #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "Authenticator Ilovasi Faolsizlantirildi" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "Yangi elektron kalit qo‘shildi." #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "Elektron Kalit Qo'shildi" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "Elektron kalit olib tashlandi." #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "Elektron Kalit Olib Tashlandi" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "Autentifikatsiya Ilovasi" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "Autentifikatsiya ilovasi orqali autentifikatsiya faol." #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "Autentifikatsiya ilovasi faol emas." #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "Faolsizlantirish" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "Faollashtirish" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "Xavfsizlik Kalitlari" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "%(count)s ta xavfsizlik kaliti qo'shildi." #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "Hech qanday xavfsizlik kalitlari qo'shilmadi." #: templates/mfa/index.html:62 msgid "Manage" msgstr "Boshqarish" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "Qo'shish" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "Tiklash Kodlari" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "%(total_count)s ta tiklash kodidan %(unused_count)s tasi mavjud." #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "Qayta tiklash kodlari sozlanmagan." #: templates/mfa/index.html:96 msgid "View" msgstr "Ko‘rish" #: templates/mfa/index.html:102 msgid "Download" msgstr "Yuklab Olish" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "Yaratish" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "Qayta tiklash kodlarining yangi to'plami yaratildi." #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "Elektron kalit qo'shildi." #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "Elektron kalit olib tashlandi." #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "Autentifikatsiya kodini kiriting:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "Akkauntingiz uchun yangi tiklash kodlari toʻplamini yaratasiz." #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "Ushbu harakat mavjud kodlaringizni bekor qiladi." #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "Ishonchingiz komilmi?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "Ishlatilmagan kodlar" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "Kodlarni yuklab olish" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "Yangi kodlarni hosil qilish" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "Autentifikatsiya Ilovasini Faollashtirish" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "Akkauntingizni ikki-bosqichli autentifikatsiya bilan himoya qilish uchun " "autentifikatsiya ilovasi yordamida quyidagi QR kodni skanerlang. Keyin ilova " "tomonidan yaratilgan tasdiqlash kodini kiriting." #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Autentifikatsiya kaliti" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "" "Siz ushbu kalitni saqlashingiz va undan keyinroq autentifikatsiya " "ilovangizni qayta oʻrnatish uchun foydalanishingiz mumkin." #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "Autentifikatsiya Ilovasini Faolsizlantirish" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "" "Siz autentifikatsiya qiluvchi ilovaga asoslangan autentifikatsiyani o'chirib " "qo'ymoqchisiz. Ishonchingiz komilmi?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "Ushbu brauzerga ishonasizmi?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "" "Agar siz ushbu brauzerga ishonishni tanlasangiz, keyingi safar tizimga " "kirganingizda tasdiqlash kodi so'ralmaydi." #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "%(period)s davomida ishonish" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "Ishonmaslik" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "Elektron Kalit Qo'shish" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "Elektron Kalitni Olib Tashlash" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "Haqiqatan ham bu elektron kalitni olib tashlamoqchimisiz?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "Qo'llanilish" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "O'tish kaliti" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "Xavfsizlik kaliti" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "Bu kalit uning o'tish kaliti yoki yo'qligini bildirmaydi." #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "Aniqlanmagan" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "%(created_at)s da qoʻshilgan" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "%(last_used)s da oxirgi marta ishlatilgan" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "O'zgartirish" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "Elektron Kalitni O'zgartirish" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "Saqlash" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "Parol kalitini yaratish" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "Siz akkauntingiz uchun parol kalitini yaratmoqchisiz. Keyinchalik qo'shimcha " "kalitlarni qo'shishingiz mumkin bo'lganligi sababli, kalitlarni ajratish " "uchun tavsifiy nom ishlatishingiz mumkin." #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "Yaratish" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "Bu funksiya JavaScript-ni talab qiladi." #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "Uchinchi Tomon Tizimga Kirishda Xato" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "" "Uchinchi tomon akkauntingiz orqali kirishga urinishda xatolik yuz berdi." #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "" "Siz quyidagi uchinchi tomon akkauntlaridan foydalanib akkauntingizga " "kirishingiz mumkin:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "Sizda hozirda ushbu akkauntga ulangan uchinchi tomon akkauntlari yo'q." #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "Uchinchi Tomon Aakkauntini Qo'shish" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "" "%(provider)s kompaniyasining uchinchi tomon akkaunti sizning akkauntingizga " "ulandi." #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "Uchinchi Tomon Akkaunti Ulandi" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "" "%(provider)s kompaniyasining uchinchi tomon akkaunti sizning akkauntingizdan " "uzildi." #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "Uchinchi Tomon Akkaunti Uzildi" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "%(provider)sni Ulash" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "Siz %(provider)s dan yangi uchinchi tomon akkauntini ulamoqchisiz." #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "%(provider)s Orqali Kirish" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "" "Siz %(provider)s dan uchinchi tomon akkaunti orqali tizimga kirmoqchisiz." #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "Kirish Bekor Qilindi" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "Siz mavjud akkauntlaringizdan biri yordamida saytimizga kirishni bekor " "qilishga qaror qildingiz. Agar bu xato boʻlsa, kirishga oʻting." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "Uchinchi tomon akkaunti ulandi." #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "Uchinchi tomon akkaunti uzildi." #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "Siz %(site_name)s saytiga kirish uchun\n" "%(provider_name)s akkauntingizdan foydalanmoqchisiz. Yakuniy qadam sifatida " "quyidagi formani to‘ldiring:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "Yoki uchinchi tomondan foydalaning" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "Boshqa barcha seanslardan chiqildi." #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "Boshlangan" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP Manzil" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "Brauzer" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "Oxirgi marta ko'rilgan" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "Hozirgi" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "Boshqa Seanslardan Chiqish" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "Foydalanuvchi Seanslari" #: usersessions/models.py:94 msgid "session key" msgstr "seans kaliti" #~ msgid "Account Connection" #~ msgstr "Akkauntga Bog'lanish" ================================================ FILE: allauth/locale/zh_Hans/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-09-15 15:01+0000\n" "Last-Translator: Cassian \n" "Language-Team: Chinese (Simplified Han script) \n" "Language: zh_Hans\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 5.14-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "此账号当前未激活。" #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "您不能删除您的主要电子邮件地址 (%(email)s)。" #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "此电子邮件地址已关联到这个账号。" #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "您提供的电子邮件地址和/或密码不正确。" #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "您输入的电话号码和/或密码不正确。" #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "此电子邮件地址已被其他用户注册。" #: account/adapter.py:75 msgid "Please type your current password." msgstr "请输入您的当前密码。" #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "错误代码。" #: account/adapter.py:77 msgid "Incorrect password." msgstr "无效密码。" #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "令牌无效或过期。" #: account/adapter.py:79 msgid "Invalid login." msgstr "登录无效。" #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "您重置密码的token是无效的。" #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "您不能添加超过%d个电子邮件地址。" #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "此电话号码已被其他用户注册。" #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "登录失败次数过多,请稍后重试。" #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "此电子邮件地址未分配给任何用户账号。" #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "此电话号码未分配给任何用户账号。" #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "您的主要电子邮件地址必须被验证。" #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "此用户名不能使用,请改用其他用户名。" #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "您提供的用户名或密码不正确。" #: account/adapter.py:98 msgid "Please select only one." msgstr "请仅选择一项。" #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "新的输入值必须不同于当前的内容。" #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "请稍等,您已经提交过多次请求。" #: account/adapter.py:826 msgid "Use your password" msgstr "使用您的密码" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "请使用认证APP或代码" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "使用安全密钥" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "已将 {email} 标记为已验证。" #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "标记 {email} 为已验证失败。" #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "将选中的电子邮件地址标记为已验证" #: account/apps.py:11 msgid "Accounts" msgstr "账户" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "电子邮件" #: account/fields.py:19 msgid "Email address" msgstr "电子邮件地址" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "请输入包含国家/地区代码的电话号码(例如,美国为 +1)。" #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "电话号码" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "每次输入的密码必须相同。" #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "密码" #: account/forms.py:67 msgid "Remember Me" msgstr "记住我" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "用户名" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "登录" #: account/forms.py:115 msgid "Username, email or phone" msgstr "用户名、电子邮件或电话号码" #: account/forms.py:117 msgid "Username or email" msgstr "用户名或email" #: account/forms.py:119 msgid "Username or phone" msgstr "用户名或电话号码" #: account/forms.py:121 msgid "Email or phone" msgstr "电子邮件或电话号码" #: account/forms.py:144 msgid "Forgot your password?" msgstr "忘记密码?" #: account/forms.py:287 msgid "Email (again)" msgstr "电子邮件(再次输入)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "电子邮件地址确认" #: account/forms.py:302 msgid "Email (optional)" msgstr "电子邮件 (选填项)" #: account/forms.py:314 msgid "Username (optional)" msgstr "用户名(选填项)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "每次输入的电子邮件必须相同。" #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "密码(重复)" #: account/forms.py:591 msgid "Current Password" msgstr "当前密码" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "新密码" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "新密码(重复)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "代码" #: account/models.py:23 msgid "user" msgstr "用户" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "电子邮件地址" #: account/models.py:31 msgid "verified" msgstr "已验证" #: account/models.py:32 msgid "primary" msgstr "主要" #: account/models.py:38 msgid "email addresses" msgstr "电子邮件地址" #: account/models.py:142 msgid "created" msgstr "已建立" #: account/models.py:143 msgid "sent" msgstr "已发送" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "密钥" #: account/models.py:149 msgid "email confirmation" msgstr "电子邮件确认" #: account/models.py:150 msgid "email confirmations" msgstr "电子邮件确认" #: headless/apps.py:7 msgid "Headless" msgstr "无头" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "查看您的用户 ID" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "查看您的电子邮件地址" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "查看您的基本个人资料信息" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "授予权限" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "除非启用“允许 URI 通配符”,否则不允许使用通配符。" #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "URI“{}”包含多个通配符 (*)。每个 URI 只允许一个通配符。" #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "通配符只允许在 URI 的主机名部分使用。" #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "验证码" #: idp/oidc/models.py:38 msgid "Device code" msgstr "设备代码" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "客户端密钥" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "刷新令牌" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "机密" #: idp/oidc/models.py:44 msgid "Public" msgstr "公共" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "客户端被允许请求的范围。每行提供一个值,例如:openid(回车)profile(回" "车)email(回车)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "如果客户端没有指定任何范围,将使用这些默认范围。每行提供一个值,例如:" "openid(回车)profile(回车)email(回车)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "允许的授权类型列表。每行提供一个值,例如:authorization_code(回" "车)client_credentials(回车)refresh_token(回车)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "跨域请求允许的源列表,每行一个。" #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "允许在重定向 URI 和 CORS 来源中使用通配符 (*)。启用后,URI 可以包含一个星号来" "匹配子域名。" #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "允许的响应类型列表。每行提供一个值,例如:code(回车)id_token token(回车)" #: idp/oidc/models.py:115 msgid "client" msgstr "客户端" #: idp/oidc/models.py:116 msgid "clients" msgstr "客户端" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "您不能在开启了两步验证的账户中添加电子邮件地址。" #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "您不能停用两步验证。" #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "未启用两步验证,您无法生成恢复代码。" #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "在验证您的Email地址之前,您不能激活两步验证。" #: mfa/adapter.py:141 msgid "Master key" msgstr "主密钥" #: mfa/adapter.py:143 msgid "Backup key" msgstr "备份密钥" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "密钥 {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "多因素认证" #: mfa/models.py:24 msgid "Recovery codes" msgstr "恢复代码" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP 认证器" #: mfa/models.py:26 msgid "WebAuthn" msgstr "WebAuthn" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "认证器代码" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "无密码" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "启用无密码操作允许您仅使用此密钥登录,但会提出其他要求,例如生物特征或PIN保" "护。" #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "已有一个账号与此Email地址关联。请先登录该账号,然后连接您的 %s 账号。" #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "令牌无效。" #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "您的账号未设置密码。" #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "您的账号下无任何验证过的email地址。" #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "您不能断开您剩下的唯一的第三方账号。" #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "该第三方账号已连接到另一个账户。" #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "社交账户" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "提供者" #: socialaccount/models.py:53 msgid "provider ID" msgstr "提供者ID" #: socialaccount/models.py:57 msgid "name" msgstr "名字" #: socialaccount/models.py:59 msgid "client id" msgstr "客户端ID" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "应用ID或消费者密钥" #: socialaccount/models.py:64 msgid "secret key" msgstr "密钥" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API密钥、客户端密钥或消费者密钥" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "密钥" #: socialaccount/models.py:82 msgid "social application" msgstr "社交应用" #: socialaccount/models.py:83 msgid "social applications" msgstr "社交应用程序" #: socialaccount/models.py:118 msgid "uid" msgstr "用户ID" #: socialaccount/models.py:120 msgid "last login" msgstr "最后一次登录" #: socialaccount/models.py:121 msgid "date joined" msgstr "加入日期" #: socialaccount/models.py:122 msgid "extra data" msgstr "额外数据" #: socialaccount/models.py:126 msgid "social account" msgstr "社交账户" #: socialaccount/models.py:127 msgid "social accounts" msgstr "社交账号" #: socialaccount/models.py:161 msgid "token" msgstr "令牌" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "\"oauth_token\" (OAuth1) 或访问令牌 (OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "令牌密钥" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "\"oauth_token_secret\" (OAuth1) 或刷新令牌 (OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "过期时间" #: socialaccount/models.py:175 msgid "social application token" msgstr "社交应用令牌" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "社交应用令牌" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "无效个人资料数据" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "登录" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "取消" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "在请求token时收到无效的响应\"%s\"。响应如下:%s。" #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "从 \"%s\" 获取访问令牌时响应无效。" #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "未保存 \"%s\" 的请求令牌。" #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "未保存 \"%s\" 的访问令牌。" #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "无权访问私有资源 \"%s\"。" #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "从 \"%s\" 获取请求令牌时响应无效。" #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "账号未激活" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "此账号未激活。" #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "我们已发送一个代码到 %(recipient)s。该代码将很快过期,请尽快输入。" #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "确认" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "请求新代码" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "确认访问" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "请重新验证以保护您的账户安全。" #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "可选项" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "电子邮件验证" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "输入电子邮件验证码:" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "使用不同的电子邮件地址" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "登录" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "输入登录代码" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "密码重置" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "输入密码重置代码" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "电话验证" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "输入电话验证代码:" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "使用不同的电话号码" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "Email地址" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "以下Email地址已关联到您的帐号:" #: templates/account/email.html:25 msgid "Verified" msgstr "已验证" #: templates/account/email.html:29 msgid "Unverified" msgstr "未验证" #: templates/account/email.html:34 msgid "Primary" msgstr "首选Email" #: templates/account/email.html:44 msgid "Make Primary" msgstr "设置为首选" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "重发验证Email" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "移除" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "添加Email地址" #: templates/account/email.html:70 msgid "Add Email" msgstr "添加Email" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "您真的想移除选定的Email地址吗?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "您收到这封电子邮件是因为您或其他人尝试使用以下电子邮件地址注册账户:\n" "\n" "%(email)s\n" "\n" "但是,使用该电子邮件地址的账户已经存在。如果您忘记了这一点,请使用密码找回程" "序来恢复您的账户:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "账户已经存在" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "来自%(site_name)s的 Hello!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "感谢您使用 %(site_name)s!\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "您收到这封邮件是因为您的账户产生了以下变动:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "如果这不是您做出的更改,请立即采取适当的安全措施。对您的账户的更改来自:\n" "\n" "- IP地址: %(ip)s\n" "- 浏览器: %(user_agent)s\n" "- 日期: %(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "您的电子邮件已从 %(from_email)s 更改为 %(to_email)s。" #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "Email已更改" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "您的Email已经被确认。" #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "Email确认" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "您收到此电子邮件是因为用户 %(user_display)s 使用您的电子邮件地址在 " "%(site_domain)s 上注册了一个账户。" #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "您的电子邮件验证代码如下所示。请在打开的浏览器窗口中输入。" #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "要确认这是正确的,请访问 %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "请确认您的Email地址" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "Email地址 %(deleted_email)s 已从您的账户中移除。" #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Email被移除了" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "您的登录代码如下所示。请在打开的浏览器窗口中输入。" #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "如果您没有发起这个操作,您可以安全地忽略这封邮件。" #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "登录代码" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "您的密码现已被修改。" #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "密码已更改" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "您的密码重置代码如下所示。请在打开的浏览器窗口中输入。" #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "密码重置代码" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "您收到此电子邮件是因为您或其他人申请了重置您的用户账户密码。\n" "如果您没有申请重置密码,您可以安全地忽略此邮件。点击下面的链接以重置您的密" "码。" #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "作为提示,您的用户名是%(username)s." #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "密码重置邮件" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "您的密码已被重置。" #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "您的密码已被设置。" #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "密码设置" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "您收到这封邮件是因为您或其他人尝试使用电子邮件 %(email)s 访问一个账户。然而," "我们的数据库中没有这个账户的任何记录。" #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "如果是您本人,您可以使用下面的链接注册一个账户。" #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "未知账号" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "Email地址" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "当前Email" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "改为" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "您的电子邮件地址仍在等待验证。" #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "取消变更" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "改为" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "更改Email" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "确认Email地址" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "请确认%(email)s是用户%(user_display)s的电子" "邮件地址。" #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "无法确认%(email)s,因为它已被另一个账户确认。" #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "这个电子邮件确认链接已过期或无效。请发起新的电子邮" "件确认请求。" #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "如果您还没有创建账号,请先 %(link)s注册%(end_link)s 。" #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "使用 passkey 登录" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "发送登录代码给我" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "登出" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "您确定要登出吗?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "您不能删除您的主Email地址 (%(email)s) 。" #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "确认邮件已发往 %(email)s。" #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "您已确认Email地址 %(email)s。" #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "已移除Email地址 %(email)s。" #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "以 %(name)s 成功登录。" #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "您已登出。" #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "登录代码已发送至 %(recipient)s。" #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "密码修改成功。" #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "密码设置成功。" #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "验证码已发送至 %(phone)s。" #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "您已验证电话号码 %(phone)s。" #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "主Email地址已设置。" #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "修改密码" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "忘记密码?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "忘记您的密码了吗?请在下面输入您的电子邮件地址,我们将发送一封电子邮件,让您" "可以重置密码。" #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "重置我的密码" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "如在重置密码时遇到问题,请与我们联系。" #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "我们已经给您发送了一封电子邮件。如果您还没有收到,请检查您的垃圾邮件文件夹。" "否则,如果几分钟内仍未收到,请联系我们。" #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "不正确的 token" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "密码重置链接无效,可能是因为它已被使用。请申请新的密码重置。" #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "您的密码现已被修改。" #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "设置密码" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "更改电话" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "当前电话" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "您的电话号码仍在等待验证。" #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "更改电话" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "输入您的密码:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "您将收到一个用于免密码登录的特殊代码。" #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "请求码" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "其它登录选项" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "注册" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "注册" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "已经有一个账号? 请%(link)s登录%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "使用 passkey 注册" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "Passkey 注册" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "其它选项" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "注册关闭" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "非常抱歉,当前已关闭注册。" #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "注意" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "您已以 %(user_display)s 的身份登录。" #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "警告:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "您当前未设置任何电子邮件地址。您真的应该添加一个电子邮件地址,以便接收通知," "重置密码等。" #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "验证您的Email地址" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "我们已向您发送了一封验证邮件。请按照邮件中提供的链接完成注册流程。如果您在主" "收件箱中看不到验证邮件,请检查您的垃圾邮件文件夹。如果几分钟内还没有收到验证" "邮件,请联系我们。" #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "本站的这部分需要我们验证您的身份真实性。为此,我们需要您验证您的电子邮件地址" "的所有权。" #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "我们已向您发送了一封用于验证的电子邮件。请点击邮件内的链接。如果您在主收件箱" "中找不到该验证邮件,请检查您的垃圾邮件文件夹。否则,如果几分钟内还未收到,请" "联系我们。" #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "注意:您仍然可以更改您的电子邮件地" "址。" #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "消息:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "菜单:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "账号连接" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "双因素身份认证" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "会话" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "授权" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s 想要访问您的 %(site_name)s 账户。" #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "输入设备代码" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "输入您设备上显示的代码。" #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "继续" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "确认设备" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "请确认您的 %(client_name)s 上显示的代码以授权此设备。" #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "拒绝" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "设备已授权" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "您已成功授权您的 %(client_name)s 设备。" #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "设备已拒绝" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "您的 %(client_name)s 设备的授权已被拒绝。" #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "错误" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "保持登录" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "您的帐户已受到双因素身份认证的保护。请输入身份验证器代码:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "已生成一组新的双因素身份认证恢复代码。" #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "已生成新的恢复代码" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "身份验证器应用已激活。" #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "身份验证器应用已激活" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "身份验证器应用已停用。" #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "身份验证器应用已停用" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "已添加新的安全密钥。" #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "已添加新的密钥" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "一个安全密钥已被移除。" #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "密钥已被移除" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "身份验证器应用" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "使用身份验证器应用进行身份验证处于活动状态。" #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "未激活身份验证器应用。" #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "停用" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "激活" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "安全密钥" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "您已添加%(count)s个安全密钥。" #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "未添加安全密钥。" #: templates/mfa/index.html:62 msgid "Manage" msgstr "管理" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "添加" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "恢复代码" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "在 %(total_count)s个恢复码中,有 %(unused_count)s 个可用。" #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "未设置恢复代码。" #: templates/mfa/index.html:96 msgid "View" msgstr "查看" #: templates/mfa/index.html:102 msgid "Download" msgstr "下载" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "生成" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "已生成新的恢复代码集。" #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "已添加安全密钥。" #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "已移除安全密钥。" #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "输入身份验证器代码:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "您即将为您的帐户生成一组新的恢复代码。" #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "此操作将使您现有的代码失效。" #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "确定要执行此操作吗?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "未使用的代码" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "下载代码" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "生成新代码" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "激活身份验证器应用" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "为使用双因素验证保护您的帐户,您需要使用身份验证器应用扫描下方的二维码。然" "后,在下方输入应用生成的验证代码。" #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "身份验证器密钥" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "您可以保存此密钥,以便以后重新安装您的身份验证器应用程序。" #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "停用身份验证器应用" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "您即将停用基于身份验证器应用的身份验证。确定要执行此操作吗?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "信任此浏览器?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "如果您选择信任此浏览器,下次登录时将不会要求您输入验证码。" #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "信任 %(period)s" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "不信任" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "添加安全密钥" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "移除安全密钥" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "您确定要移除此安全密钥吗?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "用途" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "Passkey" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "安全密钥" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "此密钥未表明其是否为 passkey。" #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "未指定" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "添加于 %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "上次使用:%(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "编辑" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "编辑安全密钥" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "保存" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "创建 Passkey" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "您即将为您的账户创建一个 passkey。由于您以后可以添加其他密钥,因此可以使用描" "述性名称来区分密钥。" #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "创建" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "此功能需要 JavaScript。" #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "第三方登录失败" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "尝试通过您的第三方账户登录时发生错误。" #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "您可以使用以下任一第三方账号登录您的账户:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "您当前没有与此账户关联的第三方账号。" #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "添加一个第三方账号" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "已连接来自 %(provider)s 的第三方账号到您的账户。" #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "第三方账号已连接" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "已从您的账户断开来自 %(provider)s 的第三方账号连接。" #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "第三方账号已断开" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "连接 %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "您即将连接一个新的来自 %(provider)s 的第三方账号。" #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "通过 %(provider)s 登录" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "您即将使用来自 %(provider)s 的第三方账号登录。" #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "登录已取消" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "您决定取消使用您的已有账号登录我们的网站。如果这是一个失误,请继续登录." #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "第三方账号已连接。" #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "第三方账号已断开连接。" #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "您即将使用您的 %(provider_name)s 账号登录到\n" "%(site_name)s。作为最后一步,请完成以下表单:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "或使用第三方" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "已退出所有其他会话。" #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "开始时间" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP地址" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "浏览器" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "上次查看于" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "当前" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "注销其他会话" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "用户会话" #: usersessions/models.py:94 msgid "session key" msgstr "会话密钥" #, fuzzy #~ msgid "Account Connection" #~ msgstr "账号连接" #~ msgid "Use security key or device" #~ msgstr "请使用安全密钥或设备" #~ msgid "Add Security Key or Device" #~ msgstr "添加安全密钥或设备" #~ msgid "Add key or device" #~ msgstr "添加密钥或设备" #~ msgid "Security Keys and Devices" #~ msgstr "安全密钥和设备" #~ msgid "You have not added any security keys/devices." #~ msgstr "您尚未添加任何安全密钥/设备。" #~ msgid "Edit Security Key or Device" #~ msgstr "编辑安全密钥或设备" ================================================ FILE: allauth/locale/zh_Hant/LC_MESSAGES/django.po ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-05 08:42-0600\n" "PO-Revision-Date: 2025-11-21 12:51+0000\n" "Last-Translator: Jeff Ma \n" "Language-Team: Chinese (Traditional Han script) \n" "Language: zh_Hant\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 5.15-dev\n" #: account/adapter.py:61 msgid "This account is currently inactive." msgstr "此帳號目前未啟用。" #: account/adapter.py:63 msgid "You cannot remove your primary email address." msgstr "您不能移除您的主要電子郵件地址。" #: account/adapter.py:66 msgid "This email address is already associated with this account." msgstr "此電子郵件地址已經與此帳號連結了。" #: account/adapter.py:69 msgid "The email address and/or password you specified are not correct." msgstr "您提供的電子郵件地址和/或密碼不正確。" #: account/adapter.py:72 msgid "The phone number and/or password you specified are not correct." msgstr "您提供的電話號碼和/或密碼不正確。" #: account/adapter.py:74 msgid "A user is already registered with this email address." msgstr "已經有使用者使用此電子郵件地址註冊。" #: account/adapter.py:75 msgid "Please type your current password." msgstr "請輸入您目前的密碼。" #: account/adapter.py:76 mfa/adapter.py:40 msgid "Incorrect code." msgstr "不正確的代碼。" #: account/adapter.py:77 msgid "Incorrect password." msgstr "不正確的密碼。" #: account/adapter.py:78 msgid "Invalid or expired key." msgstr "無效或已過期的金鑰。" #: account/adapter.py:79 msgid "Invalid login." msgstr "登入無效。" #: account/adapter.py:80 msgid "The password reset token was invalid." msgstr "密碼重置令牌無效。" #: account/adapter.py:81 #, python-format msgid "You cannot add more than %d email addresses." msgstr "您不能新增超過 %d 個電子郵件地址。" #: account/adapter.py:82 msgid "A user is already registered with this phone number." msgstr "已經有使用者使用此電話號碼註冊。" #: account/adapter.py:84 msgid "Too many failed login attempts. Try again later." msgstr "登入嘗試失敗次數過多,請稍後再試。" #: account/adapter.py:86 msgid "The email address is not assigned to any user account." msgstr "此電子郵件地址未分配給任何使用者帳號。" #: account/adapter.py:87 msgid "The phone number is not assigned to any user account." msgstr "此電話未分配給任何使用者帳號。" #: account/adapter.py:88 #: templates/account/messages/unverified_primary_email.txt:2 msgid "Your primary email address must be verified." msgstr "您必須驗證您的主要電子郵件地址。" #: account/adapter.py:90 msgid "Username can not be used. Please use other username." msgstr "無法使用此使用者名稱。請使用其他使用者名稱。" #: account/adapter.py:93 msgid "The username and/or password you specified are not correct." msgstr "您提供的使用者名稱和/或密碼不正確。" #: account/adapter.py:98 msgid "Please select only one." msgstr "請僅選擇一項。" #: account/adapter.py:99 msgid "The new value must be different from the current one." msgstr "新的輸入值必須不同於目前的內容。" #: account/adapter.py:100 msgid "Be patient, you are sending too many requests." msgstr "請稍候,您已經送出過多次的請求。" #: account/adapter.py:826 msgid "Use your password" msgstr "使用您的密碼" #: account/adapter.py:835 msgid "Use authenticator app or code" msgstr "使用驗證器應用程式或代碼" #: account/adapter.py:842 templates/mfa/authenticate.html:41 #: templates/mfa/webauthn/reauthenticate.html:15 msgid "Use a security key" msgstr "使用安全金鑰" #: account/admin.py:30 #, python-brace-format msgid "Marked {email} as verified." msgstr "已將 {email} 標記為已驗證。" #: account/admin.py:36 #, python-brace-format msgid "Failed to mark {email} as verified." msgstr "您的主要電子郵件地址{email}未標示造成驗証錯誤" #: account/admin.py:42 msgid "Mark selected email addresses as verified" msgstr "標記已選取的電子郵件地址為已驗證" #: account/apps.py:11 msgid "Accounts" msgstr "帳號" #: account/fields.py:12 account/forms.py:299 idp/oidc/forms.py:43 msgid "Email" msgstr "電子郵件" #: account/fields.py:19 msgid "Email address" msgstr "電子郵件地址" #: account/fields.py:62 msgid "Enter a phone number including country code (e.g. +1 for the US)." msgstr "請輸入包含國家碼的電話號碼 (例如:+1 美國)。" #: account/fields.py:68 account/fields.py:72 account/forms.py:325 msgid "Phone" msgstr "電話" #: account/forms.py:61 account/forms.py:524 msgid "You must type the same password each time." msgstr "您必須每次輸入相同的密碼。" #: account/forms.py:66 account/forms.py:451 account/forms.py:610 #: account/forms.py:696 msgid "Password" msgstr "密碼" #: account/forms.py:67 msgid "Remember Me" msgstr "記住我" #: account/forms.py:79 account/forms.py:82 account/forms.py:265 #: account/forms.py:268 account/forms.py:311 msgid "Username" msgstr "使用者名稱" #: account/forms.py:96 msgctxt "field label" msgid "Login" msgstr "登入" #: account/forms.py:115 msgid "Username, email or phone" msgstr "使用者名稱、電子郵件或電話號碼" #: account/forms.py:117 msgid "Username or email" msgstr "使用者名稱或電子郵件" #: account/forms.py:119 msgid "Username or phone" msgstr "使用者名稱或電話號碼" #: account/forms.py:121 msgid "Email or phone" msgstr "電子郵件或電話號碼" #: account/forms.py:144 msgid "Forgot your password?" msgstr "忘記您的密碼?" #: account/forms.py:287 msgid "Email (again)" msgstr "電子郵件(再次輸入)" #: account/forms.py:292 msgid "Email address confirmation" msgstr "電子郵件地址確認" #: account/forms.py:302 msgid "Email (optional)" msgstr "電子郵件(選擇性)" #: account/forms.py:314 msgid "Username (optional)" msgstr "使用者名稱(選填)" #: account/forms.py:388 msgid "You must type the same email each time." msgstr "您必須每次輸入相同的電子郵件。" #: account/forms.py:458 account/forms.py:611 msgid "Password (again)" msgstr "密碼(再次輸入)" #: account/forms.py:591 msgid "Current Password" msgstr "目前密碼" #: account/forms.py:593 account/forms.py:649 msgid "New Password" msgstr "新密碼" #: account/forms.py:594 account/forms.py:650 msgid "New Password (again)" msgstr "新密碼(再次輸入)" #: account/forms.py:767 account/forms.py:769 idp/oidc/forms.py:51 #: idp/oidc/forms.py:54 mfa/base/forms.py:12 mfa/base/forms.py:14 #: mfa/totp/forms.py:13 #: templates/idp/oidc/device_authorization_confirm_form.html:16 msgid "Code" msgstr "代碼" #: account/models.py:23 msgid "user" msgstr "使用者" #: account/models.py:29 account/models.py:37 account/models.py:139 msgid "email address" msgstr "電子郵件地址" #: account/models.py:31 msgid "verified" msgstr "已驗證" #: account/models.py:32 msgid "primary" msgstr "主要" #: account/models.py:38 msgid "email addresses" msgstr "電子郵件地址" #: account/models.py:142 msgid "created" msgstr "已建立" #: account/models.py:143 msgid "sent" msgstr "已傳送" #: account/models.py:144 socialaccount/models.py:70 msgid "key" msgstr "金鑰" #: account/models.py:149 msgid "email confirmation" msgstr "電子郵件確認" #: account/models.py:150 msgid "email confirmations" msgstr "電子郵件確認" #: headless/apps.py:7 msgid "Headless" msgstr "無頭" #: idp/oidc/adapter.py:30 msgid "View your user ID" msgstr "査看你使用者辨識碼" #: idp/oidc/adapter.py:31 msgid "View your email address" msgstr "查看您的電子郵件地址" #: idp/oidc/adapter.py:32 msgid "View your basic profile information" msgstr "査看你基本個人資料" #: idp/oidc/forms.py:31 msgid "Grant permissions" msgstr "使用者賦予許可" #: idp/oidc/internal/clientkit.py:22 msgid "Wildcards are not allowed unless 'Allow URI wildcards' is enabled." msgstr "除非啟用「允許 URI 萬用字元」,否則不允許使用萬用字元。" #: idp/oidc/internal/clientkit.py:27 msgid "" "URI '{}' contains more than one wildcard (*). Only one wildcard per URI is " "allowed." msgstr "URI「{}」包含多個萬用字元 (*)。每個 URI 僅允許一個萬用字元。" #: idp/oidc/internal/clientkit.py:39 msgid "Wildcards are only allowed in the hostname portion of the URI." msgstr "萬用字元僅允許在 URI 的主機名稱部分使用。" #: idp/oidc/models.py:37 msgid "Authorization code" msgstr "驗證代碼" #: idp/oidc/models.py:38 msgid "Device code" msgstr "元件代碼" #: idp/oidc/models.py:39 msgid "Client credentials" msgstr "客户信用評等" #: idp/oidc/models.py:40 msgid "Refresh token" msgstr "重置籌碼" #: idp/oidc/models.py:43 msgid "Confidential" msgstr "保密" #: idp/oidc/models.py:44 msgid "Public" msgstr "公眾" #: idp/oidc/models.py:58 msgid "" "The scope(s) the client is allowed to request. Provide one value per line, " "e.g.: openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "客戶端被允許請求的範圍。每行提供一個值,例如:openid(換行)profile(換" "行)email(換行)" #: idp/oidc/models.py:64 msgid "" "In case the client does not specify any scope, these default scopes are " "used. Provide one value per line, e.g.: " "openid(ENTER)profile(ENTER)email(ENTER)" msgstr "" "如果客戶端沒有指定任何範圍,將使用這些預設範圍。每行提供一個值,例如:" "openid(換行)profile(換行)email(換行)" #: idp/oidc/models.py:75 msgid "" "A list of allowed grant types. Provide one value per line, e.g.: " "authorization_code(ENTER)client_credentials(ENTER)refresh_token(ENTER)" msgstr "" "允許的授權類型列表。每行提供一個值,例如:authorization_code(換" "行)client_credentials(換行)refresh_token(換行)" #: idp/oidc/models.py:86 msgid "A list of allowed origins for cross-origin requests, one per line." msgstr "跨域請求允許的來源列表,每行一個。" #: idp/oidc/models.py:94 msgid "" "Allow wildcards (*) in redirect URIs and CORS origins. When enabled, URIs " "can contain a single asterisk to match subdomains." msgstr "" "允許在重新導向 URI 和 CORS 來源中使用萬用字元 (*)。啟用後,URI 可以包含一個星" "號來比對子網域。" #: idp/oidc/models.py:102 msgid "" "A list of allowed response types. Provide one value per line, e.g.: " "code(ENTER)id_token token(ENTER)" msgstr "" "允許的回應類型列表。每行提供一個值,例如:code(換行)id_token token(換行)" #: idp/oidc/models.py:115 msgid "client" msgstr "客戶端" #: idp/oidc/models.py:116 msgid "clients" msgstr "客戶端" #: mfa/adapter.py:32 msgid "" "You cannot add an email address to an account protected by two-factor " "authentication." msgstr "您不能將電子郵件地址新增至受雙重驗證保護的帳號。" #: mfa/adapter.py:35 msgid "You cannot deactivate two-factor authentication." msgstr "您不能停用雙重驗證。" #: mfa/adapter.py:38 msgid "" "You cannot generate recovery codes without having two-factor authentication " "enabled." msgstr "您不能在未啟用雙重驗證下產生復原代碼。" #: mfa/adapter.py:42 msgid "" "You cannot activate two-factor authentication until you have verified your " "email address." msgstr "您不能在驗證您的電子郵件地址之前啟用雙重驗證。" #: mfa/adapter.py:141 msgid "Master key" msgstr "主要金鑰" #: mfa/adapter.py:143 msgid "Backup key" msgstr "備份金鑰" #: mfa/adapter.py:144 #, python-brace-format msgid "Key nr. {number}" msgstr "鑰匙編號 {number}" #: mfa/apps.py:9 msgid "MFA" msgstr "多重要素驗證" #: mfa/models.py:24 msgid "Recovery codes" msgstr "恢復代碼" #: mfa/models.py:25 msgid "TOTP Authenticator" msgstr "TOTP 驗證器" #: mfa/models.py:26 msgid "WebAuthn" msgstr "網頁身份驗證" #: mfa/totp/forms.py:11 msgid "Authenticator code" msgstr "驗證器代碼" #: mfa/webauthn/forms.py:56 msgid "Passwordless" msgstr "無密碼" #: mfa/webauthn/forms.py:59 msgid "" "Enabling passwordless operation allows you to sign in using just this key, " "but imposes additional requirements such as biometrics or PIN protection." msgstr "" "啟用無密碼操作後,您可以僅使用此金鑰登入,但會要求額外的保護措施,如生物識別" "或 PIN 碼保護。" #: socialaccount/adapter.py:30 #, python-format msgid "" "An account already exists with this email address. Please sign in to that " "account first, then connect your %s account." msgstr "" "已經有一個帳號與此電子郵件連結了,請先登入該帳號,然後連接你的 %s 帳號。" #: socialaccount/adapter.py:34 msgid "Invalid token." msgstr "無效的令牌。" #: socialaccount/adapter.py:35 msgid "Your account has no password set up." msgstr "您的帳號沒有設置密碼。" #: socialaccount/adapter.py:36 msgid "Your account has no verified email address." msgstr "您的帳號下沒有驗證過的電子郵件地址。" #: socialaccount/adapter.py:38 msgid "You cannot disconnect your last remaining third-party account." msgstr "您無法切斷您最後一個的第三方帳號關聯。" #: socialaccount/adapter.py:41 #: templates/socialaccount/messages/account_connected_other.txt:2 msgid "The third-party account is already connected to a different account." msgstr "該第三方帳號已連結至其他帳戶。" #: socialaccount/apps.py:9 msgid "Social Accounts" msgstr "社群帳號" #: socialaccount/models.py:45 socialaccount/models.py:98 msgid "provider" msgstr "提供者" #: socialaccount/models.py:53 msgid "provider ID" msgstr "第三方提供者 ID" #: socialaccount/models.py:57 msgid "name" msgstr "名稱" #: socialaccount/models.py:59 msgid "client id" msgstr "client id" #: socialaccount/models.py:61 msgid "App ID, or consumer key" msgstr "應用程式 ID 或消費者金鑰" #: socialaccount/models.py:64 msgid "secret key" msgstr "秘密金鑰" #: socialaccount/models.py:67 msgid "API secret, client secret, or consumer secret" msgstr "API 秘文、客戶秘文或消費者秘文" #: socialaccount/models.py:70 templates/mfa/webauthn/authenticator_list.html:18 msgid "Key" msgstr "金鑰" #: socialaccount/models.py:82 msgid "social application" msgstr "社群應用程式" #: socialaccount/models.py:83 msgid "social applications" msgstr "社群應用程式" #: socialaccount/models.py:118 msgid "uid" msgstr "使用者 ID" #: socialaccount/models.py:120 msgid "last login" msgstr "最後一次登入" #: socialaccount/models.py:121 msgid "date joined" msgstr "加入日期" #: socialaccount/models.py:122 msgid "extra data" msgstr "額外資料" #: socialaccount/models.py:126 msgid "social account" msgstr "社群帳號" #: socialaccount/models.py:127 msgid "social accounts" msgstr "社群帳號" #: socialaccount/models.py:161 msgid "token" msgstr "令牌" #: socialaccount/models.py:162 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)" msgstr "「oauth_token」(OAuth1)或存取 token(OAuth2)" #: socialaccount/models.py:166 msgid "token secret" msgstr "token 密鑰" #: socialaccount/models.py:167 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)" msgstr "「oauth_token_secret」(OAuth1)或刷新 token(OAuth2)" #: socialaccount/models.py:170 msgid "expires at" msgstr "過期日" #: socialaccount/models.py:175 msgid "social application token" msgstr "社群應用程式 Token" #: socialaccount/models.py:176 msgid "social application tokens" msgstr "社群應用程式 Token" #: socialaccount/providers/douban/views.py:35 msgid "Invalid profile data" msgstr "無效的資料" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:16 msgid "Login" msgstr "登入" #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:19 #: templates/account/base_confirm_code.html:48 #: templates/account/base_confirm_code.html:52 #: templates/account/password_reset_from_key.html:33 #: templates/idp/oidc/authorization_form.html:26 #: templates/mfa/authenticate.html:28 templates/mfa/authenticate.html:44 #: templates/mfa/trust.html:29 templates/mfa/webauthn/signup_form.html:26 msgid "Cancel" msgstr "取消" #: socialaccount/providers/oauth/client.py:86 #, python-format msgid "" "Invalid response while obtaining request token from \"%s\". Response was: %s." msgstr "從 \"%s\" 獲取請求令牌時回應無效。回應內容為:%s。" #: socialaccount/providers/oauth/client.py:121 #: socialaccount/providers/pocket/client.py:81 #, python-format msgid "Invalid response while obtaining access token from \"%s\"." msgstr "從 \"%s\" 獲取 access token 時回應無效。" #: socialaccount/providers/oauth/client.py:142 #, python-format msgid "No request token saved for \"%s\"." msgstr "未為 \"%s\" 儲存請求 token。" #: socialaccount/providers/oauth/client.py:193 #, python-format msgid "No access token saved for \"%s\"." msgstr "未為 \"%s\" 儲存 access token。" #: socialaccount/providers/oauth/client.py:215 #, python-format msgid "No access to private resources at \"%s\"." msgstr "無權訪問私有資源 \"%s\"。" #: socialaccount/providers/pocket/client.py:39 #, python-format msgid "Invalid response while obtaining request token from \"%s\"." msgstr "從 \"%s\" 獲取 request token 時回應無效。" #: templates/account/account_inactive.html:5 #: templates/account/account_inactive.html:9 msgid "Account Inactive" msgstr "帳號未啟用" #: templates/account/account_inactive.html:12 msgid "This account is inactive." msgstr "此帳號未啟用。" #: templates/account/base_confirm_code.html:27 #, python-format msgid "" "We've sent a code to %(recipient)s. The code expires shortly, so please " "enter it soon." msgstr "我們已經代碼傳送給%(recipient)s.此代碼有效期限極短,請盡速輸入。" #: templates/account/base_confirm_code.html:39 #: templates/account/email_confirm.html:24 #: templates/account/reauthenticate.html:18 #: templates/idp/oidc/device_authorization_confirm_form.html:26 #: templates/mfa/reauthenticate.html:18 msgid "Confirm" msgstr "確認" #: templates/account/base_confirm_code.html:43 msgid "Request new code" msgstr "請求新的代碼" #: templates/account/base_reauthenticate.html:5 #: templates/account/base_reauthenticate.html:9 msgid "Confirm Access" msgstr "確認存取權限" #: templates/account/base_reauthenticate.html:12 msgid "Please reauthenticate to safeguard your account." msgstr "請重新認證以保護您的帳號。" #: templates/account/base_reauthenticate.html:19 #: templates/mfa/authenticate.html:37 msgid "Alternative options" msgstr "替代選項" #: templates/account/confirm_email_verification_code.html:5 msgid "Email Verification" msgstr "電子郵件驗證" #: templates/account/confirm_email_verification_code.html:8 msgid "Enter Email Verification Code" msgstr "輸入電子郵件驗證碼" #: templates/account/confirm_email_verification_code.html:16 msgid "Use a different email address" msgstr "使用不同的電子郵件地址" #: templates/account/confirm_login_code.html:5 templates/account/login.html:5 #: templates/account/login.html:9 templates/account/login.html:31 #: templates/account/request_login_code.html:5 #: templates/allauth/layouts/base.html:68 templates/mfa/authenticate.html:6 #: templates/mfa/authenticate.html:24 templates/openid/login.html:5 #: templates/openid/login.html:9 templates/openid/login.html:20 #: templates/socialaccount/login.html:5 #: templates/socialaccount/login_redirect.html:5 msgid "Sign In" msgstr "登入" #: templates/account/confirm_login_code.html:8 msgid "Enter Sign-In Code" msgstr "輸入登入碼" #: templates/account/confirm_password_reset_code.html:5 #: templates/account/email/password_reset_subject.txt:3 #: templates/account/password_reset.html:4 #: templates/account/password_reset.html:8 #: templates/account/password_reset_done.html:6 #: templates/account/password_reset_done.html:10 msgid "Password Reset" msgstr "密碼重設" #: templates/account/confirm_password_reset_code.html:8 msgid "Enter Password Reset Code" msgstr "輸入密碼重設代碼" #: templates/account/confirm_phone_verification_code.html:5 msgid "Phone Verification" msgstr "電話驗證" #: templates/account/confirm_phone_verification_code.html:8 msgid "Enter Phone Verification Code" msgstr "輸入電話驗證代碼" #: templates/account/confirm_phone_verification_code.html:15 msgid "Use a different phone number" msgstr "使用不同的電話號碼" #: templates/account/email.html:4 templates/account/email.html:8 msgid "Email Addresses" msgstr "電子郵件地址" #: templates/account/email.html:12 msgid "The following email addresses are associated with your account:" msgstr "下列電子郵件已與您的帳號連結:" #: templates/account/email.html:25 msgid "Verified" msgstr "已驗證" #: templates/account/email.html:29 msgid "Unverified" msgstr "未驗證" #: templates/account/email.html:34 msgid "Primary" msgstr "主要的" #: templates/account/email.html:44 msgid "Make Primary" msgstr "設為主要的" #: templates/account/email.html:47 templates/account/email_change.html:37 #: templates/account/phone_change.html:24 msgid "Re-send Verification" msgstr "重寄驗証信" #: templates/account/email.html:50 #: templates/mfa/webauthn/authenticator_confirm_delete.html:16 #: templates/mfa/webauthn/authenticator_list.html:60 #: templates/socialaccount/connections.html:40 msgid "Remove" msgstr "移除" #: templates/account/email.html:59 msgid "Add Email Address" msgstr "增加電子郵件" #: templates/account/email.html:70 msgid "Add Email" msgstr "增加電子郵件" #: templates/account/email.html:80 msgid "Do you really want to remove the selected email address?" msgstr "您真的要移除所選擇電子郵件嗎?" #: templates/account/email/account_already_exists_message.txt:4 #, python-format msgid "" "You are receiving this email because you or someone else tried to signup for " "an\n" "account using email address:\n" "\n" "%(email)s\n" "\n" "However, an account using that email address already exists. In case you " "have\n" "forgotten about this, please use the password forgotten procedure to " "recover\n" "your account:\n" "\n" "%(password_reset_url)s" msgstr "" "您收到此電子郵件是因為您(或其他人)嘗試使用以下電子郵件地址\n" "註冊帳戶:\n" "\n" "%(email)s\n" "\n" "然而,目前已經有使用該電子郵件地址的帳戶存在。\n" "\n" "如果您忘記了帳戶,請使用忘記密碼程序來找回您的帳戶:\n" "\n" "%(password_reset_url)s" #: templates/account/email/account_already_exists_subject.txt:3 msgid "Account Already Exists" msgstr "帳號已存在" #: templates/account/email/base_message.txt:1 #, python-format msgid "Hello from %(site_name)s!" msgstr "來自%(site_name)s!的問候!" #: templates/account/email/base_message.txt:5 #, python-format msgid "" "Thank you for using %(site_name)s!\n" "%(site_domain)s" msgstr "" "感謝您使用%(site_name)s。\n" "%(site_domain)s" #: templates/account/email/base_notification.txt:5 msgid "" "You are receiving this mail because the following change was made to your " "account:" msgstr "您收到此封郵件是因為您的帳號已做出以下變更:" #: templates/account/email/base_notification.txt:10 #, python-format msgid "" "If you do not recognize this change then please take proper security " "precautions immediately. The change to your account originates from:\n" "\n" "- IP address: %(ip)s\n" "- Browser: %(user_agent)s\n" "- Date: %(timestamp)s" msgstr "" "如果您沒有進行此變更,請立即採取適當的安全措施。\n" "此變更來源:\n" "- IP 位址:%(ip)s\n" "- 瀏覽器:%(user_agent)s\n" "- 日期:%(timestamp)s" #: templates/account/email/email_changed_message.txt:4 #, python-format msgid "Your email has been changed from %(from_email)s to %(to_email)s." msgstr "您的 Email 已從 %(from_email)s 修改成 %(to_email)s。" #: templates/account/email/email_changed_subject.txt:3 msgid "Email Changed" msgstr "電子郵件已更改" #: templates/account/email/email_confirm_message.txt:4 msgid "Your email has been confirmed." msgstr "您的電子郵件已確認。" #: templates/account/email/email_confirm_subject.txt:3 msgid "Email Confirmation" msgstr "電子郵件確認" #: templates/account/email/email_confirmation_message.txt:5 #, python-format msgid "" "You're receiving this email because user %(user_display)s has given your " "email address to register an account on %(site_domain)s." msgstr "" "您會收到此 Email 是因為使用者 %(user_display)s 提供此 Email 於網" "站%(site_domain)s 上註冊帳號。" #: templates/account/email/email_confirmation_message.txt:7 msgid "" "Your email verification code is listed below. Please enter it in your open " "browser window." msgstr "您的 Email 驗證代碼如下所列,請在您已開啟的瀏覽器視窗中輸入。" #: templates/account/email/email_confirmation_message.txt:9 #, python-format msgid "To confirm this is correct, go to %(activate_url)s" msgstr "若確認無誤,請前往 %(activate_url)s" #: templates/account/email/email_confirmation_subject.txt:3 msgid "Please Confirm Your Email Address" msgstr "確認您的電子郵件" #: templates/account/email/email_deleted_message.txt:4 #, python-format msgid "Email address %(deleted_email)s has been removed from your account." msgstr "電子郵件地址 %(deleted_email)s 已從您的帳戶中移除。" #: templates/account/email/email_deleted_subject.txt:3 msgid "Email Removed" msgstr "Email 已移除" #: templates/account/email/login_code_message.txt:5 msgid "" "Your sign-in code is listed below. Please enter it in your open browser " "window." msgstr "您的登入代碼如下所列,請在您已開啟的瀏覽器視窗中輸入。" #: templates/account/email/login_code_message.txt:9 #: templates/account/email/password_reset_code_message.txt:9 #: templates/account/email/unknown_account_message.txt:6 msgid "This mail can be safely ignored if you did not initiate this action." msgstr "若確定非您所為,請忽略此郵件。" #: templates/account/email/login_code_subject.txt:3 msgid "Sign-In Code" msgstr "登入代碼" #: templates/account/email/password_changed_message.txt:4 msgid "Your password has been changed." msgstr "您的密碼已變更。" #: templates/account/email/password_changed_subject.txt:3 msgid "Password Changed" msgstr "密碼已更改" #: templates/account/email/password_reset_code_message.txt:5 msgid "" "Your password reset code is listed below. Please enter it in your open " "browser window." msgstr "您的密碼重設代碼如下所列,請在您已開啟的瀏覽器視窗中輸入。" #: templates/account/email/password_reset_code_subject.txt:3 msgid "Password Reset Code" msgstr "密碼重設代碼" #: templates/account/email/password_reset_key_message.txt:4 msgid "" "You're receiving this email because you or someone else has requested a " "password reset for your user account.\n" "It can be safely ignored if you did not request a password reset. Click the " "link below to reset your password." msgstr "" "您收到此電子郵件是因為您或其他人已請求重設您的帳戶密碼。\n" "如果您沒有請求重設密碼,則可以安全忽略此郵件。請點擊以下連結來重設您的密碼。" #: templates/account/email/password_reset_key_message.txt:9 #, python-format msgid "In case you forgot, your username is %(username)s." msgstr "提醒您,您的使用者名稱是 %(username)s 。" #: templates/account/email/password_reset_key_subject.txt:3 msgid "Password Reset Email" msgstr "密碼重設電子郵件" #: templates/account/email/password_reset_message.txt:4 msgid "Your password has been reset." msgstr "您的密碼已被重設。" #: templates/account/email/password_set_message.txt:4 msgid "Your password has been set." msgstr "您的密碼已設定完成。" #: templates/account/email/password_set_subject.txt:3 msgid "Password Set" msgstr "密碼已設定" #: templates/account/email/unknown_account_message.txt:4 #, python-format msgid "" "You are receiving this email because you, or someone else, tried to access " "an account with email %(email)s. However, we do not have any record of such " "an account in our database." msgstr "" "您會收到此封 Email 是因為您/或是有人嘗試使用 %(email)s 此 Email 存取帳號。然" "而,我們於資料庫中找不到任何關聯的帳號紀錄。" #: templates/account/email/unknown_account_message.txt:8 msgid "If it was you, you can sign up for an account using the link below." msgstr "如果確定是您,您可以透過下列連結註冊一個帳號。" #: templates/account/email/unknown_account_subject.txt:3 msgid "Unknown Account" msgstr "未知帳戶" #: templates/account/email_change.html:5 templates/account/email_change.html:9 msgid "Email Address" msgstr "電子郵件地址" #: templates/account/email_change.html:21 #: templates/account/email_change.html:29 msgid "Current email" msgstr "目前電子郵件" #: templates/account/email_change.html:31 msgid "Changing to" msgstr "變更為" #: templates/account/email_change.html:35 msgid "Your email address is still pending verification." msgstr "您的電子郵件地址仍在等待驗證。" #: templates/account/email_change.html:41 msgid "Cancel Change" msgstr "取消變更" #: templates/account/email_change.html:49 #: templates/account/phone_change.html:32 msgid "Change to" msgstr "變更為" #: templates/account/email_change.html:55 #: templates/allauth/layouts/base.html:31 msgid "Change Email" msgstr "更改電子郵件" #: templates/account/email_confirm.html:6 #: templates/account/email_confirm.html:10 msgid "Confirm Email Address" msgstr "確認電子郵件" #: templates/account/email_confirm.html:16 #, python-format msgid "" "Please confirm that %(email)s is an email " "address for user %(user_display)s." msgstr "" "請確認 %(email)s 是使用者 %(user_display)s " "所使用的電子郵件地址。" #: templates/account/email_confirm.html:30 #: templates/account/messages/email_confirmation_failed.txt:2 #, python-format msgid "" "Unable to confirm %(email)s because it is already confirmed by a different " "account." msgstr "無法驗證 %(email)s,因為該電子郵件已由其他帳戶驗證。" #: templates/account/email_confirm.html:36 #, python-format msgid "" "This email confirmation link expired or is invalid. Please issue a new email confirmation request." msgstr "" "此電子郵件確認連結已過期或無效。請重新發送電子郵件" "確認請求。" #: templates/account/login.html:19 #, python-format msgid "" "If you have not created an account yet, then please %(link)ssign " "up%(end_link)s first." msgstr "若您沒有帳號,請先 %(link)s註冊%(end_link)s 。" #: templates/account/login.html:42 msgid "Sign in with a passkey" msgstr "使用通行密鑰登入" #: templates/account/login.html:47 templates/account/request_login_code.html:9 msgid "Send me a sign-in code" msgstr "寄給我登入代碼" #: templates/account/logout.html:4 templates/account/logout.html:8 #: templates/account/logout.html:21 templates/allauth/layouts/base.html:61 #: templates/idp/oidc/logout.html:4 templates/idp/oidc/logout.html:8 #: templates/idp/oidc/logout.html:21 #: templates/usersessions/usersession_list.html:76 msgid "Sign Out" msgstr "登出" #: templates/account/logout.html:11 templates/idp/oidc/logout.html:11 msgid "Are you sure you want to sign out?" msgstr "您確定要登出嗎?" #: templates/account/messages/cannot_delete_primary_email.txt:2 #, python-format msgid "You cannot remove your primary email address (%(email)s)." msgstr "您不能移除您的主要的電子郵件地址 (%(email)s) 。" #: templates/account/messages/email_confirmation_sent.txt:2 #, python-format msgid "Confirmation email sent to %(email)s." msgstr "確認信已發至 %(email)s 。" #: templates/account/messages/email_confirmed.txt:2 #, python-format msgid "You have confirmed %(email)s." msgstr "您已確認 %(email)s。" #: templates/account/messages/email_deleted.txt:2 #, python-format msgid "Removed email address %(email)s." msgstr "電子郵件地址 %(email)s 已刪除。" #: templates/account/messages/logged_in.txt:4 #, python-format msgid "Successfully signed in as %(name)s." msgstr "成功以 %(name)s..的身份登入。" #: templates/account/messages/logged_out.txt:2 msgid "You have signed out." msgstr "您已登出。" #: templates/account/messages/login_code_sent.txt:2 #, python-format msgid "A sign-in code has been sent to %(recipient)s." msgstr "登入代碼已經寄給 %(recipient)s。" #: templates/account/messages/password_changed.txt:2 msgid "Password successfully changed." msgstr "密碼修改完成。" #: templates/account/messages/password_set.txt:2 msgid "Password successfully set." msgstr "密碼設定完成。" #: templates/account/messages/phone_verification_sent.txt:2 #, python-format msgid "A verification code has been sent to %(phone)s." msgstr "登入代碼已經寄給 %(phone)s。" #: templates/account/messages/phone_verified.txt:2 #, python-format msgid "You have verified phone number %(phone)s." msgstr "您已完成驗證電話號碼 %(phone)s 。" #: templates/account/messages/primary_email_set.txt:2 msgid "Primary email address set." msgstr "已設定好主要的電子郵件地址。" #: templates/account/password_change.html:4 #: templates/account/password_change.html:8 #: templates/account/password_change.html:20 #: templates/account/password_reset_from_key.html:5 #: templates/account/password_reset_from_key.html:12 #: templates/account/password_reset_from_key.html:30 #: templates/account/password_reset_from_key_done.html:5 #: templates/account/password_reset_from_key_done.html:9 #: templates/allauth/layouts/base.html:37 msgid "Change Password" msgstr "修改密碼" #: templates/account/password_change.html:22 msgid "Forgot Password?" msgstr "忘記密碼了?" #: templates/account/password_reset.html:14 msgid "" "Forgotten your password? Enter your email address below, and we'll send you " "an email allowing you to reset it." msgstr "" "忘記您的密碼了嗎? 請在下方輸入您的電子郵件,我們會發送一封電子郵件給您,以便" "重新設定您的密碼。" #: templates/account/password_reset.html:26 msgid "Reset My Password" msgstr "重設我的密碼" #: templates/account/password_reset.html:31 msgid "Please contact us if you have any trouble resetting your password." msgstr "如果在重設密碼時碰到問題,請與我們聯絡。" #: templates/account/password_reset_done.html:16 msgid "" "We have sent you an email. If you have not received it please check your " "spam folder. Otherwise contact us if you do not receive it in a few minutes." msgstr "" "我們已經寄送了一封電子郵件給您。如果您沒有收到,請檢查您的垃圾郵件資料夾。如" "果仍未收到,請在幾分鐘後聯繫我們。" #: templates/account/password_reset_from_key.html:10 msgid "Bad Token" msgstr "無效的 token" #: templates/account/password_reset_from_key.html:18 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " "used. Please request a new password reset." msgstr "" "密碼重設連結已失效,可能是因為該連結已經被人用過了,請重新申請重設密碼。" #: templates/account/password_reset_from_key_done.html:12 msgid "Your password is now changed." msgstr "您的密碼已變更。" #: templates/account/password_set.html:5 templates/account/password_set.html:9 #: templates/account/password_set.html:21 msgid "Set Password" msgstr "設定密碼" #: templates/account/phone_change.html:5 templates/account/phone_change.html:9 msgid "Change Phone" msgstr "更改電話號碼" #: templates/account/phone_change.html:18 msgid "Current phone" msgstr "目前電話號碼" #: templates/account/phone_change.html:22 msgid "Your phone number is still pending verification." msgstr "您的電話號碼仍在等待驗證。" #: templates/account/phone_change.html:38 msgctxt "action" msgid "Change Phone" msgstr "更改電話號碼" #: templates/account/reauthenticate.html:6 msgid "Enter your password:" msgstr "輸入您的密碼:" #: templates/account/request_login_code.html:12 msgid "You will receive a special code for a password-free sign-in." msgstr "您將會收到一組特殊代碼用於免密碼登入。" #: templates/account/request_login_code.html:24 msgid "Request Code" msgstr "請求代碼" #: templates/account/request_login_code.html:30 msgid "Other sign-in options" msgstr "其他登入選項" #: templates/account/signup.html:4 templates/account/signup_by_passkey.html:4 #: templates/socialaccount/signup.html:5 msgid "Signup" msgstr "註冊" #: templates/account/signup.html:8 templates/account/signup.html:30 #: templates/account/signup_by_passkey.html:29 #: templates/allauth/layouts/base.html:74 templates/socialaccount/signup.html:9 #: templates/socialaccount/signup.html:25 msgid "Sign Up" msgstr "註冊" #: templates/account/signup.html:17 templates/account/signup_by_passkey.html:17 #, python-format msgid "Already have an account? Then please %(link)ssign in%(end_link)s." msgstr "已有帳號了嗎?請%(link)s登入%(end_link)s." #: templates/account/signup.html:39 msgid "Sign up using a passkey" msgstr "使用通行密鑰註冊" #: templates/account/signup_by_passkey.html:8 msgid "Passkey Sign Up" msgstr "使用通行密鑰註冊" #: templates/account/signup_by_passkey.html:36 msgid "Other options" msgstr "其他選項" #: templates/account/signup_closed.html:5 #: templates/account/signup_closed.html:9 msgid "Sign Up Closed" msgstr "註冊未開放" #: templates/account/signup_closed.html:12 msgid "We are sorry, but the sign up is currently closed." msgstr "很抱歉,目前不開放註冊。" #: templates/account/snippets/already_logged_in.html:7 msgid "Note" msgstr "注意" #: templates/account/snippets/already_logged_in.html:7 #, python-format msgid "You are already logged in as %(user_display)s." msgstr "您已經以 %(user_display)s 的身份登入了。" #: templates/account/snippets/warn_no_email.html:3 msgid "Warning:" msgstr "警告:" #: templates/account/snippets/warn_no_email.html:3 msgid "" "You currently do not have any email address set up. You should really add an " "email address so you can receive notifications, reset your password, etc." msgstr "" "您尚未設定任何電子郵件。建議您最好設定一個電子郵件,以便您接收通知或重新設定" "密碼等等。" #: templates/account/verification_sent.html:5 #: templates/account/verification_sent.html:9 #: templates/account/verified_email_required.html:5 #: templates/account/verified_email_required.html:9 msgid "Verify Your Email Address" msgstr "驗證您的電子郵件地址" #: templates/account/verification_sent.html:12 msgid "" "We have sent an email to you for verification. Follow the link provided to " "finalize the signup process. If you do not see the verification email in " "your main inbox, check your spam folder. Please contact us if you do not " "receive the verification email within a few minutes." msgstr "" "我們已經向您發送了一封電子郵件進行驗證。請按照郵件中的連結完成註冊過程。如果" "您在主要收件匣中沒有看到驗證郵件,請檢查您的垃圾郵件資料夾。如果幾分鐘後仍未" "收到驗證郵件,請與我們聯繫。" #: templates/account/verified_email_required.html:13 msgid "" "This part of the site requires us to verify that\n" "you are who you claim to be. For this purpose, we require that you\n" "verify ownership of your email address. " msgstr "" "此網站的此部分要求我們驗證您的身份。\n" "為此,\n" "我們要求您驗證您的電子郵件地址的所有權。 " #: templates/account/verified_email_required.html:18 msgid "" "We have sent an email to you for\n" "verification. Please click on the link inside that email. If you do not see " "the verification email in your main inbox, check your spam folder. " "Otherwise\n" "contact us if you do not receive it within a few minutes." msgstr "" "我們已經向您發送了一封電子郵件進行驗證。\n" "請點擊郵件中的連結。如果您在主要收件匣中沒有看到驗證郵件,請檢查您的垃圾郵件" "資料夾。\n" "如果幾分鐘後仍未收到,請與我們聯繫。" #: templates/account/verified_email_required.html:23 #, python-format msgid "" "Note: you can still change your " "email address." msgstr "" "注意: 您仍能修改您的電子郵件地址 " "." #: templates/allauth/layouts/base.html:18 msgid "Messages:" msgstr "訊息:" #: templates/allauth/layouts/base.html:25 msgid "Menu:" msgstr "選單:" #: templates/allauth/layouts/base.html:43 #: templates/socialaccount/connections.html:5 #: templates/socialaccount/connections.html:9 msgid "Account Connections" msgstr "帳號連結" #: templates/allauth/layouts/base.html:49 templates/mfa/authenticate.html:10 #: templates/mfa/index.html:5 templates/mfa/index.html:9 msgid "Two-Factor Authentication" msgstr "二階段認證" #: templates/allauth/layouts/base.html:55 #: templates/usersessions/usersession_list.html:6 #: templates/usersessions/usersession_list.html:10 msgid "Sessions" msgstr "登入階段" #: templates/idp/oidc/authorization_form.html:5 #: templates/idp/oidc/authorization_form.html:9 #: templates/idp/oidc/authorization_form.html:23 #: templates/idp/oidc/device_authorization_code_form.html:5 #: templates/idp/oidc/device_authorization_confirm_form.html:5 #: templates/idp/oidc/device_authorization_confirmed.html:5 #: templates/idp/oidc/device_authorization_denied.html:5 msgid "Authorize" msgstr "授權" #: templates/idp/oidc/authorization_form.html:12 #, python-format msgid "%(client_name)s wants to access your %(site_name)s account." msgstr "%(client_name)s 想要存取您的 %(site_name)s 帳戶。" #: templates/idp/oidc/device_authorization_code_form.html:9 msgid "Enter Device Code" msgstr "輸入裝置代碼" #: templates/idp/oidc/device_authorization_code_form.html:12 msgid "Enter the code displayed on your device." msgstr "輸入您裝置上顯示的代碼。" #: templates/idp/oidc/device_authorization_code_form.html:21 #: templates/socialaccount/login.html:27 #: templates/socialaccount/login_redirect.html:10 msgid "Continue" msgstr "繼續" #: templates/idp/oidc/device_authorization_confirm_form.html:9 msgid "Confirm Device" msgstr "確認裝置" #: templates/idp/oidc/device_authorization_confirm_form.html:12 #, python-format msgid "" "Please confirm the code shown on your %(client_name)s to authorize this " "device." msgstr "請確認您的 %(client_name)s 上顯示的代碼以授權此裝置。" #: templates/idp/oidc/device_authorization_confirm_form.html:29 msgid "Deny" msgstr "拒絕" #: templates/idp/oidc/device_authorization_confirmed.html:9 msgid "Device Authorized" msgstr "裝置已授權" #: templates/idp/oidc/device_authorization_confirmed.html:12 #, python-format msgid "You successfully authorized your %(client_name)s device." msgstr "您已成功授權您的 %(client_name)s 裝置。" #: templates/idp/oidc/device_authorization_denied.html:9 msgid "Device Denied" msgstr "裝置已拒絕" #: templates/idp/oidc/device_authorization_denied.html:12 #, python-format msgid "Authorization for your %(client_name)s device has been denied." msgstr "您的 %(client_name)s 裝置的授權已被拒絕。" #: templates/idp/oidc/error.html:5 templates/idp/oidc/error.html:9 msgid "Error" msgstr "錯誤" #: templates/idp/oidc/logout.html:24 msgid "Stay Signed In" msgstr "保持登入" #: templates/mfa/authenticate.html:13 msgid "" "Your account is protected by two-factor authentication. Please enter an " "authenticator code:" msgstr "您的帳號由二階驗證所保護,請輸入驗證器中的代碼:" #: templates/mfa/email/recovery_codes_generated_message.txt:4 msgid "" "A new set of Two-Factor Authentication recovery codes has been generated." msgstr "新的二階驗證復原代碼已建立。" #: templates/mfa/email/recovery_codes_generated_subject.txt:3 msgid "New Recovery Codes Generated" msgstr "新的復原代碼已建立" #: templates/mfa/email/totp_activated_message.txt:4 #: templates/mfa/messages/totp_activated.txt:2 msgid "Authenticator app activated." msgstr "驗證器已啟用。" #: templates/mfa/email/totp_activated_subject.txt:3 msgid "Authenticator App Activated" msgstr "驗證器已啟用" #: templates/mfa/email/totp_deactivated_message.txt:4 #: templates/mfa/messages/totp_deactivated.txt:2 msgid "Authenticator app deactivated." msgstr "驗證器已停用。" #: templates/mfa/email/totp_deactivated_subject.txt:3 msgid "Authenticator App Deactivated" msgstr "驗證器已停用" #: templates/mfa/email/webauthn_added_message.txt:4 msgid "A new security key has been added." msgstr "新的安全金鑰已成功新增。" #: templates/mfa/email/webauthn_added_subject.txt:3 msgid "Security Key Added" msgstr "已新增安全金鑰" #: templates/mfa/email/webauthn_removed_message.txt:4 msgid "A security key has been removed." msgstr "安全金鑰已被移除。" #: templates/mfa/email/webauthn_removed_subject.txt:3 msgid "Security Key Removed" msgstr "安全金鑰已移除" #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4 msgid "Authenticator App" msgstr "驗證器" #: templates/mfa/index.html:19 msgid "Authentication using an authenticator app is active." msgstr "已啟用驗證器進行驗證。" #: templates/mfa/index.html:23 msgid "An authenticator app is not active." msgstr "authenticator 驗證器應用程式未啟用。" #: templates/mfa/index.html:32 templates/mfa/totp/deactivate_form.html:24 msgid "Deactivate" msgstr "停用" #: templates/mfa/index.html:36 templates/mfa/totp/activate_form.html:32 msgid "Activate" msgstr "啟用" #: templates/mfa/index.html:45 templates/mfa/webauthn/authenticator_list.html:8 #: templates/mfa/webauthn/base.html:4 msgid "Security Keys" msgstr "安全金鑰" #: templates/mfa/index.html:50 #, python-format msgid "You have added %(count)s security key." msgid_plural "You have added %(count)s security keys." msgstr[0] "您已新增 %(count)s 個安全金鑰。" #: templates/mfa/index.html:54 #: templates/mfa/webauthn/authenticator_list.html:12 msgid "No security keys have been added." msgstr "尚未新增任何安全金鑰。" #: templates/mfa/index.html:62 msgid "Manage" msgstr "管理" #: templates/mfa/index.html:67 templates/mfa/webauthn/add_form.html:18 #: templates/mfa/webauthn/authenticator_list.html:70 msgid "Add" msgstr "新增" #: templates/mfa/index.html:77 templates/mfa/recovery_codes/base.html:4 #: templates/mfa/recovery_codes/generate.html:6 #: templates/mfa/recovery_codes/index.html:6 msgid "Recovery Codes" msgstr "復原碼" #: templates/mfa/index.html:82 templates/mfa/recovery_codes/index.html:9 #, python-format msgid "" "There is %(unused_count)s out of %(total_count)s recovery codes available." msgid_plural "" "There are %(unused_count)s out of %(total_count)s recovery codes available." msgstr[0] "共有 %(total_count)s 個恢復碼,其中剩餘 %(unused_count)s 個可用。" #: templates/mfa/index.html:86 msgid "No recovery codes set up." msgstr "尚未設定恢復碼。" #: templates/mfa/index.html:96 msgid "View" msgstr "檢視" #: templates/mfa/index.html:102 msgid "Download" msgstr "下載" #: templates/mfa/index.html:110 templates/mfa/recovery_codes/generate.html:29 msgid "Generate" msgstr "產生" #: templates/mfa/messages/recovery_codes_generated.txt:2 msgid "A new set of recovery codes has been generated." msgstr "已產生一組新的恢復碼。" #: templates/mfa/messages/webauthn_added.txt:2 msgid "Security key added." msgstr "已成功新增安全金鑰。" #: templates/mfa/messages/webauthn_removed.txt:2 msgid "Security key removed." msgstr "安全金鑰已成功移除。" #: templates/mfa/reauthenticate.html:6 msgid "Enter an authenticator code:" msgstr "輸入驗證器代碼:" #: templates/mfa/recovery_codes/generate.html:9 msgid "You are about to generate a new set of recovery codes for your account." msgstr "您即將為您的帳戶產生一組新的恢復碼。" #: templates/mfa/recovery_codes/generate.html:11 msgid "This action will invalidate your existing codes." msgstr "此操作將使您現有的恢復碼失效。" #: templates/mfa/recovery_codes/generate.html:13 msgid "Are you sure?" msgstr "您確定嗎?" #: templates/mfa/recovery_codes/index.html:13 msgid "Unused codes" msgstr "未使用之代碼" #: templates/mfa/recovery_codes/index.html:25 msgid "Download codes" msgstr "下載代碼" #: templates/mfa/recovery_codes/index.html:30 msgid "Generate new codes" msgstr "產生新的代碼" #: templates/mfa/totp/activate_form.html:4 #: templates/mfa/totp/activate_form.html:8 msgid "Activate Authenticator App" msgstr "啟用驗證器" #: templates/mfa/totp/activate_form.html:11 msgid "" "To protect your account with two-factor authentication, scan the QR code " "below with your authenticator app. Then, input the verification code " "generated by the app below." msgstr "" "為了使用雙重驗證保護您的帳戶,請使用您的驗證器應用程式掃描下方的 QR 碼,然後" "輸入應用程式產生的驗證碼。" #: templates/mfa/totp/activate_form.html:21 msgid "Authenticator secret" msgstr "Authenticator 驗證器密鑰" #: templates/mfa/totp/activate_form.html:24 msgid "" "You can store this secret and use it to reinstall your authenticator app at " "a later time." msgstr "您可以儲存此密鑰,稍後重新安裝 authenticator 驗證器應用程式時使用。" #: templates/mfa/totp/deactivate_form.html:5 #: templates/mfa/totp/deactivate_form.html:9 msgid "Deactivate Authenticator App" msgstr "停用驗證器" #: templates/mfa/totp/deactivate_form.html:12 msgid "" "You are about to deactivate authenticator app based authentication. Are you " "sure?" msgstr "您即將停用基於 authenticator 驗證器應用程式的身份驗證。確定要繼續嗎?" #: templates/mfa/trust.html:4 templates/mfa/trust.html:8 msgid "Trust this Browser?" msgstr "信任此瀏覽器?" #: templates/mfa/trust.html:11 msgid "" "If you choose to trust this browser, you will not be asked for a " "verification code the next time you sign in." msgstr "如果您選擇信任此瀏覽器,下次登入時將不會要求輸入驗證碼。" #: templates/mfa/trust.html:23 #, python-format msgid "Trust for %(period)s" msgstr "於 %(period)s 期間信任" #: templates/mfa/trust.html:26 msgid "Don't Trust" msgstr "不要信任" #: templates/mfa/webauthn/add_form.html:7 msgid "Add Security Key" msgstr "新增安全金鑰" #: templates/mfa/webauthn/authenticator_confirm_delete.html:6 msgid "Remove Security Key" msgstr "移除安全金鑰" #: templates/mfa/webauthn/authenticator_confirm_delete.html:9 msgid "Are you sure you want to remove this security key?" msgstr "您確定要移除此安全金鑰嗎?" #: templates/mfa/webauthn/authenticator_list.html:21 msgid "Usage" msgstr "使用方式" #: templates/mfa/webauthn/authenticator_list.html:33 msgid "Passkey" msgstr "通行密鑰" #: templates/mfa/webauthn/authenticator_list.html:37 msgid "Security key" msgstr "安全金鑰" #: templates/mfa/webauthn/authenticator_list.html:40 msgid "This key does not indicate whether it is a passkey." msgstr "此金鑰無法判斷是否為通行密鑰(Passkey)。" #: templates/mfa/webauthn/authenticator_list.html:41 msgid "Unspecified" msgstr "未指定" #: templates/mfa/webauthn/authenticator_list.html:46 #, python-format msgid "Added on %(created_at)s" msgstr "新增於 %(created_at)s" #: templates/mfa/webauthn/authenticator_list.html:48 #, python-format msgid "Last used %(last_used)s" msgstr "上次使用時間:%(last_used)s" #: templates/mfa/webauthn/authenticator_list.html:56 msgid "Edit" msgstr "編輯" #: templates/mfa/webauthn/edit_form.html:7 msgid "Edit Security Key" msgstr "編輯安全金鑰" #: templates/mfa/webauthn/edit_form.html:18 msgid "Save" msgstr "儲存" #: templates/mfa/webauthn/signup_form.html:7 msgid "Create Passkey" msgstr "建立通行密鑰" #: templates/mfa/webauthn/signup_form.html:10 msgid "" "You are about to create a passkey for your account. As you can add " "additional keys later on, you can use a descriptive name to tell the keys " "apart." msgstr "" "您即將為您的帳戶建立通行密鑰(Passkey)。由於您可以稍後新增其他金鑰,建議使用" "具描述性的名稱來區分不同金鑰。" #: templates/mfa/webauthn/signup_form.html:21 msgid "Create" msgstr "建立" #: templates/mfa/webauthn/snippets/scripts.html:2 msgid "This functionality requires JavaScript." msgstr "此功能需要JavaScript支援。" #: templates/socialaccount/authentication_error.html:5 #: templates/socialaccount/authentication_error.html:9 msgid "Third-Party Login Failure" msgstr "第三方帳號登入失敗" #: templates/socialaccount/authentication_error.html:12 msgid "" "An error occurred while attempting to login via your third-party account." msgstr "嘗試使用您的第三方帳號登入時發生錯誤。" #: templates/socialaccount/connections.html:13 msgid "" "You can sign in to your account using any of the following third-party " "accounts:" msgstr "您可以使用下列任何第三方帳號登入您的帳號:" #: templates/socialaccount/connections.html:46 msgid "You currently have no third-party accounts connected to this account." msgstr "您目前沒有任何第三方帳號與此帳號連結。" #: templates/socialaccount/connections.html:50 msgid "Add a Third-Party Account" msgstr "新增一個第三方帳號" #: templates/socialaccount/email/account_connected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been connected to your account." msgstr "來自 %(provider)s 的第三方帳號已連結至您的帳號。" #: templates/socialaccount/email/account_connected_subject.txt:3 msgid "Third-Party Account Connected" msgstr "已連接第三方帳號" #: templates/socialaccount/email/account_disconnected_message.txt:4 #, python-format msgid "" "A third-party account from %(provider)s has been disconnected from your " "account." msgstr "已移除來自 %(provider)s 的第三方帳戶與您的帳戶關聯。" #: templates/socialaccount/email/account_disconnected_subject.txt:3 msgid "Third-Party Account Disconnected" msgstr "已取消第三方帳號關聯" #: templates/socialaccount/login.html:10 #, python-format msgid "Connect %(provider)s" msgstr "連接 %(provider)s" #: templates/socialaccount/login.html:13 #, python-format msgid "You are about to connect a new third-party account from %(provider)s." msgstr "您即將連結來自 %(provider)s 的新第三方帳戶。" #: templates/socialaccount/login.html:17 #, python-format msgid "Sign In Via %(provider)s" msgstr "透過 %(provider)s 登入" #: templates/socialaccount/login.html:20 #, python-format msgid "You are about to sign in using a third-party account from %(provider)s." msgstr "您即將使用來自 %(provider)s 的第三方帳號登入。" #: templates/socialaccount/login_cancelled.html:5 #: templates/socialaccount/login_cancelled.html:9 msgid "Login Cancelled" msgstr "登入已取消" #: templates/socialaccount/login_cancelled.html:13 #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " "accounts. If this was a mistake, please proceed to sign in." msgstr "" "您決定不繼續登入這一個網站。若這是一個失誤,請由此" "重新登入。" #: templates/socialaccount/messages/account_connected.txt:2 msgid "The third-party account has been connected." msgstr "已建立第三方帳號關聯。" #: templates/socialaccount/messages/account_disconnected.txt:2 msgid "The third-party account has been disconnected." msgstr "已中斷第三方帳號關聯。" #: templates/socialaccount/signup.html:12 #, python-format msgid "" "You are about to use your %(provider_name)s account to login to\n" "%(site_name)s. As a final step, please complete the following form:" msgstr "" "您即將使用您的 %(provider_name)s 帳戶登入\n" "%(site_name)s。作為最後一步,請完成以下表單:" #: templates/socialaccount/snippets/login.html:10 msgid "Or use a third-party" msgstr "或是使用第三方" #: templates/usersessions/messages/sessions_logged_out.txt:2 msgid "Signed out of all other sessions." msgstr "登出所有其他登入階段。" #: templates/usersessions/usersession_list.html:24 msgid "Started At" msgstr "開始於" #: templates/usersessions/usersession_list.html:27 msgid "IP Address" msgstr "IP 地址" #: templates/usersessions/usersession_list.html:30 msgid "Browser" msgstr "瀏覽器" #: templates/usersessions/usersession_list.html:34 msgid "Last seen at" msgstr "最後出現於" #: templates/usersessions/usersession_list.html:59 msgid "Current" msgstr "目前" #: templates/usersessions/usersession_list.html:72 msgid "Sign Out Other Sessions" msgstr "登出其他使用階段" #: usersessions/apps.py:9 msgid "User Sessions" msgstr "使用者會話" #: usersessions/models.py:94 msgid "session key" msgstr "會話金鑰" #, fuzzy #~ msgid "Account Connection" #~ msgstr "帳號連結" #~ msgid "Use security key or device" #~ msgstr "使用安全金鑰或裝置" #, python-brace-format #~ msgid "Password must be a minimum of {0} characters." #~ msgstr "密碼長度至少要有 {0} 個字元。" #, fuzzy, python-format #~ msgid "" #~ "You are receiving this email because you or someone else has requested a\n" #~ "password for your user account. However, we do not have any record of a " #~ "user\n" #~ "with email %(email)s in our database.\n" #~ "\n" #~ "This mail can be safely ignored if you did not request a password reset.\n" #~ "\n" #~ "If it was you, you can sign up for an account using the link below." #~ msgstr "" #~ "您會收到這封信是因為您或是某人在 %(site_domain)s 這個網站上要求重設您帳號" #~ "的密碼。\n" #~ "若您沒有要求我們重設密碼,請您直接忽略這封信。若要重設您的密碼,請點擊下面" #~ "的連結。" #, fuzzy #~ msgid "The following email address is associated with your account:" #~ msgstr "下列電子郵件已與你的帳號連結:" #, fuzzy #~ msgid "Change Email Address" #~ msgstr "確認電子郵件" #, fuzzy, python-format #~ msgid "" #~ "Please sign in with one\n" #~ "of your existing third party accounts. Or, sign up\n" #~ "for a %(site_name)s account and sign in below:" #~ msgstr "" #~ "請用您的第三方帳號登入。\n" #~ "或者%(link)s註冊%(end_link)s \n" #~ "一個 %(site_name)s帳號後登入:" #~ msgid "or" #~ msgstr "或" #~ msgid "change password" #~ msgstr "修改密碼" #~ msgid "OpenID Sign In" #~ msgstr "OpenID 登入" #~ msgid "This email address is already associated with another account." #~ msgstr "此電子郵件已經與別的帳號連結了。" #~ msgid "" #~ "We have sent you an e-mail. Please contact us if you do not receive it " #~ "within a few minutes." #~ msgstr "" #~ "我們已經寄了一封電子郵件給您,如果數分鐘內您沒有收到,請與我們聯絡。" #~ msgid "The login and/or password you specified are not correct." #~ msgstr "您提供的帳號或密碼不正確。" #~ msgid "Usernames can only contain letters, digits and @/./+/-/_." #~ msgstr "使用者名稱只能包含字母,數字及 @/./+/-/_." #~ msgid "This username is already taken. Please choose another." #~ msgstr "這個使用者名稱已經有人用了,請換一個。" #~ msgid "" #~ "You have confirmed that %(email)s is an " #~ "e-mail address for user %(user_display)s." #~ msgstr "" #~ "您以確認%(email)s是使用者%(user_display)s" #~ "的電子郵件地址。" #~ msgid "Thanks for using our site!" #~ msgstr "感謝您使用我們的網站!" #~ msgid "Confirmation email sent to %(email)s" #~ msgstr "确认e-mail已发往 %(email)s" #~ msgid "Delete Password" #~ msgstr "删除密码" #~ msgid "" #~ "You may delete your password since you are currently logged in using " #~ "OpenID." #~ msgstr "您当前使用OpenID登录,因此您可以删除你的密码。" #~ msgid "delete my password" #~ msgstr "删除我的密码" #~ msgid "Password Deleted" #~ msgstr "密码已删除" #~ msgid "" #~ "If you have any trouble resetting your password, contact us at %(CONTACT_EMAIL)s." #~ msgstr "" #~ "Als je problemen hebt je wachtwoord opnieuw in te stellen, neem dan " #~ "contact op met %(CONTACT_EMAIL)s." #~ msgid "OpenID" #~ msgstr "OpenID" #~ msgid "Already have an account?" #~ msgstr "Heb je al een account?" #~ msgid "Sign in" #~ msgstr "Aanmelden" #~ msgid "Language" #~ msgstr "Taal" #~ msgid "Pinax can be used in your preferred language." #~ msgstr "Deze site kan in jouw voorkeurstaal gebruikt worden." #~ msgid "Change my language" #~ msgstr "Verander mijn taal" #~ msgid "Timezone" #~ msgstr "Tijdzone" #, fuzzy #~ msgid "" #~ "You're receiving this e-mail because you requested a password reset\n" #~ "for your user account at Pinax.\n" #~ "\n" #~ "Your new password is: %(new_password)s\n" #~ "\n" #~ "Your username, in case you've forgotten: %(username)s\n" #~ "\n" #~ "You should log in as soon as possible and change your password.\n" #~ "\n" #~ "Thanks for using our site!\n" #~ msgstr "" #~ "Je ontvangt deze mail omdat er een verzoek is ingelegd om het wachtwoord\n" #~ "behorende bij je %(site_name)s account opnieuw in te stellen.\n" #~ "\n" #~ "Je nieuwe wachtwoord is: %(new_password)s\n" #~ "\n" #~ "Je gebruikersnaam, voor het geval je die vergeten bent, is: %(username)s\n" #~ "\n" #~ "Je moet zo snel mogelijk inloggen en bovenstaand wachtwoord veranderen.\n" #~ "\n" #~ "Bedankt voor het gebruik van onze site!\n" #~ msgid "If checked you will stay logged in for 3 weeks" #~ msgstr "Bij 'Onthouden' blijf je ingelogd gedurende 3 weken" #~ msgid "Timezone successfully updated." #~ msgstr "Tijdzone gewijzigd." #~ msgid "Language successfully updated." #~ msgstr "Taal gewijzigd." #~ msgid "None" #~ msgstr "Geen" #~ msgid "Log In" #~ msgstr "Inloggen" #~ msgid "Log in" #~ msgstr "Inloggen" #~ msgid "Logout" #~ msgstr "Afmelden" #~ msgid "" #~ "When you receive the new password, you should log in and change it as soon as possible." #~ msgstr "" #~ "Zodra je het nieuwe wachtwoord ontvangen hebt moet je zo snel mogelijk inloggen en het wachtwoord wijzigen." #~ msgid "You are already logged in." #~ msgstr "Je bent al ingelogd." #~ msgid "" #~ "By clicking \"Sign Up\", you are indicating that you have read and agree " #~ "to the Terms of Use and Privacy Policy." #~ msgstr "" #~ "Door te registreren geef je aan dat je de gebruiksvoorwaarden en de privacy policy gelezen hebt en ermee akkoord gaat." #~ msgid "" #~ "If you have any trouble creating your account, contact us at %(contact_email)s." #~ msgstr "" #~ "Als je problemen hebt om een account aan te maken, neem dan contact op " #~ "met %(contact_email)s." #~ msgid "Log in »" #~ msgstr "Inloggen" ================================================ FILE: allauth/mfa/__init__.py ================================================ ================================================ FILE: allauth/mfa/adapter.py ================================================ from io import BytesIO from urllib.parse import quote, urlencode from django.utils.translation import gettext, gettext_lazy as _ from allauth import app_settings as allauth_settings from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.utils import ( user_display, user_email, user_pk_to_url_str, user_username, ) from allauth.core import context from allauth.core.internal.adapter import BaseAdapter from allauth.mfa import app_settings from allauth.mfa.models import Authenticator from allauth.utils import import_attribute class DefaultMFAAdapter(BaseAdapter): """The adapter class allows you to override various functionality of the ``allauth.mfa`` app. To do so, point ``settings.MFA_ADAPTER`` to your own class that derives from ``DefaultMFAAdapter`` and override the behavior by altering the implementation of the methods according to your own needs. """ error_messages = { "add_email_blocked": _( "You cannot add an email address to an account protected by two-factor authentication." ), "cannot_delete_authenticator": _( "You cannot deactivate two-factor authentication." ), "cannot_generate_recovery_codes": _( "You cannot generate recovery codes without having two-factor authentication enabled." ), "incorrect_code": _("Incorrect code."), "unverified_email": _( "You cannot activate two-factor authentication until you have verified your email address." ), } "The error messages that can occur as part of MFA form handling." def get_totp_label(self, user) -> str: """Returns the label used for representing the given user in a TOTP QR code. """ return self._get_user_identifier(user) def _get_user_identifier(self, user) -> str: """Human-palatable identifier for a user account. It is intended only for display. """ label = user_email(user) if not label: label = user_username(user) if not label: label = str(user) return label def get_totp_issuer(self) -> str: """Returns the TOTP issuer name that will be contained in the TOTP QR code. """ issuer = app_settings.TOTP_ISSUER if not issuer: issuer = self._get_site_name() return issuer def build_totp_url(self, user, secret: str) -> str: label = self.get_totp_label(user) issuer = self.get_totp_issuer() params = { "secret": secret, # This is the default # "algorithm": "SHA1", "issuer": issuer, } if app_settings.TOTP_DIGITS != 6: params["digits"] = app_settings.TOTP_DIGITS if app_settings.TOTP_PERIOD != 30: params["period"] = app_settings.TOTP_PERIOD return f"otpauth://totp/{quote(label)}?{urlencode(params, quote_via=quote)}" def build_totp_svg(self, url: str) -> str: import qrcode from qrcode.image.svg import SvgPathImage img = qrcode.make(url, image_factory=SvgPathImage) buf = BytesIO() img.save(buf) return buf.getvalue().decode("utf8") def _get_site_name(self) -> str: if allauth_settings.SITES_ENABLED: from django.contrib.sites.models import Site return Site.objects.get_current(context.request).name else: return context.request.get_host() def encrypt(self, text: str) -> str: """Secrets such as the TOTP key are stored in the database. This hook can be used to encrypt those so that they are not stored in the clear in the database. """ return text def decrypt(self, encrypted_text: str) -> str: """Counter part of ``encrypt()``.""" text = encrypted_text return text def can_delete_authenticator(self, authenticator: Authenticator) -> bool: return True def send_notification_mail(self, *args, **kwargs): return get_account_adapter().send_notification_mail(*args, **kwargs) def is_mfa_enabled(self, user, types=None) -> bool: """ Returns ``True`` if (and only if) the user has 2FA enabled. """ if user.is_anonymous: return False qs = Authenticator.objects.filter(user=user) if types is not None: qs = qs.filter(type__in=types) return qs.exists() def generate_authenticator_name(self, user, type: Authenticator.Type) -> str: """ Generate a human friendly name for the key. Used to prefill the "Add key" form. """ n = Authenticator.objects.filter(user=user, type=type).count() if n == 0: return gettext("Master key") elif n == 1: return gettext("Backup key") return gettext("Key nr. {number}").format(number=n + 1) def get_public_key_credential_rp_entity(self) -> dict[str, str]: name = self._get_site_name() return { "id": context.request.get_host().partition(":")[0], "name": name, } def get_public_key_credential_user_entity(self, user) -> dict: return { "id": user_pk_to_url_str(user).encode("utf8"), "display_name": user_display(user), "name": self._get_user_identifier(user), } def get_adapter() -> DefaultMFAAdapter: return import_attribute(app_settings.ADAPTER)() ================================================ FILE: allauth/mfa/admin.py ================================================ from django.contrib import admin from allauth.mfa.models import Authenticator @admin.register(Authenticator) class AuthenticatorAdmin(admin.ModelAdmin): raw_id_fields = ("user",) list_display = ("user", "type", "created_at", "last_used_at") list_filter = ("type", "created_at", "last_used_at") ================================================ FILE: allauth/mfa/app_settings.py ================================================ from datetime import timedelta class AppSettings: def __init__(self, prefix: str) -> None: self.prefix = prefix def _setting(self, name: str, dflt): from allauth.utils import get_setting return get_setting(f"{self.prefix}{name}", dflt) @property def ADAPTER(self) -> str: return self._setting("ADAPTER", "allauth.mfa.adapter.DefaultMFAAdapter") @property def ALLOW_UNVERIFIED_EMAIL(self) -> bool: return self._setting("ALLOW_UNVERIFIED_EMAIL", False) @property def FORMS(self) -> dict: return self._setting("FORMS", {}) @property def RECOVERY_CODE_COUNT(self) -> int: """ The number of recovery codes. """ return self._setting("RECOVERY_CODE_COUNT", 10) @property def RECOVERY_CODE_DIGITS(self) -> int: """ The number of digits of each recovery code. """ return self._setting("RECOVERY_CODE_DIGITS", 8) @property def TOTP_PERIOD(self) -> int: """ The period that a TOTP code will be valid for, in seconds. """ return self._setting("TOTP_PERIOD", 30) @property def TOTP_DIGITS(self) -> int: """ The number of digits for TOTP codes """ return self._setting("TOTP_DIGITS", 6) @property def TOTP_ISSUER(self) -> str: """ The issuer. """ return self._setting("TOTP_ISSUER", "") @property def TOTP_INSECURE_BYPASS_CODE(self): """ Don't use this on production. Useful for development & E2E tests only. """ from django.conf import settings from django.core.exceptions import ImproperlyConfigured code = self._setting("TOTP_INSECURE_BYPASS_CODE", None) if (not settings.DEBUG) and code: raise ImproperlyConfigured( "MFA_TOTP_INSECURE_BYPASS_CODE is for testing purposes only" ) return code @property def TOTP_TOLERANCE(self) -> int: """ The number of time steps in the past or future to allow. Lower values are more secure, but more likely to fail due to clock drift. """ return self._setting("TOTP_TOLERANCE", 0) @property def SUPPORTED_TYPES(self) -> list[str]: dflt = ["recovery_codes", "totp"] return self._setting("SUPPORTED_TYPES", dflt) @property def WEBAUTHN_ALLOW_INSECURE_ORIGIN(self) -> bool: return self._setting("WEBAUTHN_ALLOW_INSECURE_ORIGIN", False) @property def PASSKEY_LOGIN_ENABLED(self) -> bool: return "webauthn" in self.SUPPORTED_TYPES and self._setting( "PASSKEY_LOGIN_ENABLED", False ) @property def PASSKEY_SIGNUP_ENABLED(self) -> bool: return "webauthn" in self.SUPPORTED_TYPES and self._setting( "PASSKEY_SIGNUP_ENABLED", False ) @property def TRUST_ENABLED(self) -> bool: return self._setting("TRUST_ENABLED", False) @property def _TRUST_STAGE_ENABLED(self) -> bool: from allauth.account import app_settings as account_settings return self.TRUST_ENABLED or account_settings.LOGIN_BY_CODE_TRUST_ENABLED @property def TRUST_COOKIE_AGE(self) -> timedelta: age = self._setting("TRUST_COOKIE_AGE", timedelta(days=14)) if not isinstance(age, timedelta): age = timedelta(seconds=age) return age @property def TRUST_COOKIE_NAME(self) -> str: return self._setting("TRUST_COOKIE_NAME", "mfa_trusted") @property def TRUST_COOKIE_DOMAIN(self) -> str | None: from django.conf import settings return self._setting("TRUST_COOKIE_DOMAIN", settings.SESSION_COOKIE_DOMAIN) @property def TRUST_COOKIE_HTTPONLY(self) -> bool: from django.conf import settings return self._setting("TRUST_COOKIE_HTTPONLY", settings.SESSION_COOKIE_HTTPONLY) @property def TRUST_COOKIE_PATH(self) -> str: from django.conf import settings return self._setting("TRUST_COOKIE_PATH", settings.SESSION_COOKIE_PATH) @property def TRUST_COOKIE_SAMESITE(self) -> str: from django.conf import settings return self._setting("TRUST_COOKIE_SAMESITE", settings.SESSION_COOKIE_SAMESITE) @property def TRUST_COOKIE_SECURE(self) -> str | None: from django.conf import settings return self._setting("TRUST_COOKIE_SECURE", settings.SESSION_COOKIE_SECURE) _app_settings = AppSettings("MFA_") def __getattr__(name): # See https://peps.python.org/pep-0562/ return getattr(_app_settings, name) ================================================ FILE: allauth/mfa/apps.py ================================================ from django.apps import AppConfig from django.utils.translation import gettext_lazy as _ from allauth import app_settings class MFAConfig(AppConfig): name = "allauth.mfa" verbose_name = _("MFA") default_auto_field = ( app_settings.DEFAULT_AUTO_FIELD or "django.db.models.BigAutoField" ) def ready(self): from allauth.account import signals as account_signals from allauth.mfa import checks # noqa from allauth.mfa import signals account_signals._add_email.connect(signals.on_add_email) ================================================ FILE: allauth/mfa/base/__init__.py ================================================ ================================================ FILE: allauth/mfa/base/forms.py ================================================ from django import forms from django.utils.translation import gettext_lazy as _ from allauth.core import context from allauth.mfa.adapter import get_adapter from allauth.mfa.base.internal.flows import check_rate_limit, post_authentication from allauth.mfa.models import Authenticator class BaseAuthenticateForm(forms.Form): code = forms.CharField( label=_("Code"), widget=forms.TextInput( attrs={"placeholder": _("Code"), "autocomplete": "one-time-code"}, ), ) def __init__(self, *args, **kwargs) -> None: self.user = kwargs.pop("user") super().__init__(*args, **kwargs) def clean_code(self) -> str: clear_rl = check_rate_limit(self.user) code = self.cleaned_data["code"] for auth in Authenticator.objects.filter(user=self.user).exclude( # WebAuthn cannot validate manual codes. type=Authenticator.Type.WEBAUTHN ): if auth.wrap().validate_code(code): auth.user = self.user self.authenticator = auth clear_rl() return code raise get_adapter().validation_error("incorrect_code") class AuthenticateForm(BaseAuthenticateForm): def save(self) -> None: post_authentication(context.request, self.authenticator) class ReauthenticateForm(BaseAuthenticateForm): def save(self) -> None: post_authentication(context.request, self.authenticator, reauthenticated=True) ================================================ FILE: allauth/mfa/base/internal/__init__.py ================================================ ================================================ FILE: allauth/mfa/base/internal/flows.py ================================================ from collections.abc import Callable from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.internal.flows.login import record_authentication from allauth.core import context, ratelimit from allauth.mfa import signals from allauth.mfa.models import Authenticator def delete_dangling_recovery_codes(user) -> Authenticator | None: deleted_authenticator = None qs = Authenticator.objects.filter(user=user) if not qs.exclude(type=Authenticator.Type.RECOVERY_CODES).exists(): deleted_authenticator = qs.first() qs.delete() return deleted_authenticator def delete_and_cleanup(request, authenticator) -> None: authenticator.delete() rc_auth = delete_dangling_recovery_codes(authenticator.user) for auth in [authenticator, rc_auth]: if auth: signals.authenticator_removed.send( sender=Authenticator, request=request, user=request.user, authenticator=auth, ) def post_authentication( request, authenticator: Authenticator, reauthenticated: bool = False, passwordless: bool = False, ) -> None: authenticator.record_usage() extra_data = { "id": authenticator.pk, "type": authenticator.type, } if reauthenticated: extra_data["reauthenticated"] = True if passwordless: extra_data["passwordless"] = True record_authentication(request, authenticator.user, "mfa", **extra_data) def check_rate_limit(user) -> Callable[[], None]: key = f"mfa-auth-user-{str(user.pk)}" if not ratelimit.consume( context.request, action="login_failed", key=key, ): raise get_account_adapter().validation_error("too_many_login_attempts") return lambda: ratelimit.clear(context.request, action="login_failed", key=key) ================================================ FILE: allauth/mfa/base/urls.py ================================================ from django.urls import URLPattern, URLResolver, path from allauth.mfa import app_settings from allauth.mfa.base import views urlpatterns: list[URLPattern | URLResolver] = [ path("", views.index, name="mfa_index"), path("authenticate/", views.authenticate, name="mfa_authenticate"), path("reauthenticate/", views.reauthenticate, name="mfa_reauthenticate"), ] if app_settings._TRUST_STAGE_ENABLED: urlpatterns.append(path("trust/", views.trust, name="mfa_trust")) ================================================ FILE: allauth/mfa/base/views.py ================================================ from django.contrib.auth.decorators import login_required from django.forms import Form from django.http import HttpResponse, HttpResponseBase, HttpResponseRedirect from django.urls import reverse from django.utils import timezone from django.utils.decorators import method_decorator from django.views.generic import TemplateView from django.views.generic.edit import FormView from allauth.account import app_settings as account_settings from allauth.account.internal.decorators import login_stage_required from allauth.account.views import BaseReauthenticateView from allauth.mfa import app_settings from allauth.mfa.base.forms import AuthenticateForm, ReauthenticateForm from allauth.mfa.internal.flows import trust as trust_ from allauth.mfa.models import Authenticator from allauth.mfa.stages import AuthenticateStage, TrustStage from allauth.mfa.utils import is_mfa_enabled from allauth.mfa.webauthn.forms import AuthenticateWebAuthnForm from allauth.mfa.webauthn.internal.flows import auth as webauthn_auth from allauth.utils import get_form_class @method_decorator( login_stage_required(stage=AuthenticateStage.key, redirect_urlname="account_login"), name="dispatch", ) class AuthenticateView(TemplateView): form_class = AuthenticateForm webauthn_form_class = AuthenticateWebAuthnForm template_name = f"mfa/authenticate.{account_settings.TEMPLATE_EXTENSION}" def dispatch(self, request, *args, **kwargs) -> HttpResponseBase: self.stage = request._login_stage if not is_mfa_enabled( self.stage.login.user, [Authenticator.Type.TOTP, Authenticator.Type.WEBAUTHN], ): return HttpResponseRedirect(reverse("account_login")) self.form = self._build_forms() return super().dispatch(request, *args, **kwargs) def post(self, request, *args, **kwargs) -> HttpResponse: if self.form.is_valid(): return self.form_valid(self.form) else: return self.form_invalid(self.form) def _build_forms(self): posted_form = None AuthenticateFormClass = self.get_form_class() AuthenticateWebAuthnFormClass = self.get_webauthn_form_class() user = self.stage.login.user support_webauthn = "webauthn" in app_settings.SUPPORTED_TYPES if self.request.method == "POST": if "code" in self.request.POST: posted_form = self.auth_form = AuthenticateFormClass( user=user, data=self.request.POST ) self.webauthn_form = ( AuthenticateWebAuthnFormClass(user=user) if support_webauthn else None ) else: self.auth_form = ( AuthenticateFormClass(user=user) if support_webauthn else None ) posted_form = self.webauthn_form = AuthenticateWebAuthnFormClass( user=user, data=self.request.POST ) else: self.auth_form = AuthenticateFormClass(user=user) self.webauthn_form = ( AuthenticateWebAuthnFormClass(user=user) if support_webauthn else None ) return posted_form def get_form_class(self): return get_form_class(app_settings.FORMS, "authenticate", self.form_class) def get_webauthn_form_class(self): return get_form_class( app_settings.FORMS, "authenticate_webauthn", self.webauthn_form_class ) def form_valid(self, form) -> HttpResponse: form.save() return self.stage.exit() def form_invalid(self, form) -> HttpResponse: return super().get(self.request) def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data() ret.update( { "form": self.auth_form, "MFA_SUPPORTED_TYPES": app_settings.SUPPORTED_TYPES, } ) if self.webauthn_form: request_options = webauthn_auth.begin_authentication(self.stage.login.user) ret.update( { "webauthn_form": self.webauthn_form, "js_data": {"request_options": request_options}, } ) return ret authenticate = AuthenticateView.as_view() @method_decorator(login_required, name="dispatch") class ReauthenticateView(BaseReauthenticateView): form_class = ReauthenticateForm template_name = f"mfa/reauthenticate.{account_settings.TEMPLATE_EXTENSION}" def get_form_kwargs(self) -> dict: ret = super().get_form_kwargs() ret["user"] = self.request.user return ret def get_form_class(self): return get_form_class(app_settings.FORMS, "reauthenticate", self.form_class) def form_valid(self, form) -> HttpResponse: form.save() return super().form_valid(form) reauthenticate = ReauthenticateView.as_view() @method_decorator(login_required, name="dispatch") class IndexView(TemplateView): template_name = f"mfa/index.{account_settings.TEMPLATE_EXTENSION}" def get_context_data(self, **kwargs): ret = super().get_context_data(**kwargs) authenticators = {} for auth in Authenticator.objects.filter(user=self.request.user): if auth.type == Authenticator.Type.WEBAUTHN: auths = authenticators.setdefault(auth.type, []) auths.append(auth.wrap()) else: authenticators[auth.type] = auth.wrap() ret["authenticators"] = authenticators ret["MFA_SUPPORTED_TYPES"] = app_settings.SUPPORTED_TYPES ret["is_mfa_enabled"] = is_mfa_enabled(self.request.user) return ret index = IndexView.as_view() @method_decorator( login_stage_required(stage=TrustStage.key, redirect_urlname="account_login"), name="dispatch", ) class TrustView(FormView): form_class = Form template_name = f"mfa/trust.{account_settings.TEMPLATE_EXTENSION}" def form_valid(self, form): do_trust = self.request.POST.get("action") == "trust" stage = self.request._login_stage response = stage.exit() if do_trust: trust_.trust_browser(self.request, stage.login.user, response) return response def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) now = timezone.now() ret["trust_from"] = now ret["trust_until"] = now + app_settings.TRUST_COOKIE_AGE return ret trust = TrustView.as_view() ================================================ FILE: allauth/mfa/checks.py ================================================ from django.core.checks import Critical, register @register() def settings_check(app_configs, **kwargs): from allauth.account import app_settings as account_settings from allauth.mfa import app_settings from allauth.mfa.models import Authenticator ret = [] if app_settings.PASSKEY_SIGNUP_ENABLED: if Authenticator.Type.WEBAUTHN not in app_settings.SUPPORTED_TYPES: ret.append( Critical( msg="MFA_PASSKEY_SIGNUP_ENABLED requires MFA_SUPPORTED_TYPES to include 'webauthn'" ) ) if not account_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED: # The fact that a signup is passkey based is stored in the session, # which gets lost when using link based verification. ret.append( Critical( msg="MFA_PASSKEY_SIGNUP_ENABLED requires ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED" ) ) email_required = account_settings.SIGNUP_FIELDS.get("email", {}).get("required") if not email_required: ret.append( Critical( msg="MFA_PASSKEY_SIGNUP_ENABLED requires ACCOUNT_SIGNUP_FIELDS to contain 'email*'" ) ) if ( account_settings.EMAIL_VERIFICATION != account_settings.EmailVerificationMethod.MANDATORY ): ret.append( Critical( msg="MFA_PASSKEY_SIGNUP_ENABLED requires ACCOUNT_EMAIL_VERIFICATION = 'mandatory'" ) ) return ret ================================================ FILE: allauth/mfa/internal/__init__.py ================================================ ================================================ FILE: allauth/mfa/internal/constants.py ================================================ from enum import Enum class LoginStageKey(str, Enum): MFA_SIGNUP_WEBAUTHN = "mfa_signup_webauthn" MFA_AUTHENTICATE = "mfa_authenticate" MFA_TRUST = "mfa_trust" ================================================ FILE: allauth/mfa/internal/flows/__init__.py ================================================ ================================================ FILE: allauth/mfa/internal/flows/add.py ================================================ from functools import wraps from django.contrib import messages from django.contrib.auth.base_user import AbstractBaseUser from django.core.exceptions import ValidationError from django.http import HttpResponseRedirect from django.urls import reverse from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.models import EmailAddress from allauth.mfa import app_settings from allauth.mfa.adapter import get_adapter def validate_can_add_authenticator(user: AbstractBaseUser) -> None: """ If we would allow users to enable 2FA with unverified email address, that would allow for an attacker to signup, not verify and prevent the real owner of the account from ever regaining access. """ if app_settings.ALLOW_UNVERIFIED_EMAIL: return email_verified = not EmailAddress.objects.filter( user_id=user.pk, verified=False ).exists() if not email_verified: raise get_adapter().validation_error("unverified_email") def redirect_if_add_not_allowed(function=None): def decorator(view_func): @wraps(view_func) def _wrapper_view(request, *args, **kwargs): if request.user.is_authenticated: # allow for this to go before reauth try: validate_can_add_authenticator(request.user) except ValidationError as e: for message in e.messages: adapter = get_account_adapter() adapter.add_message(request, messages.ERROR, message=message) return HttpResponseRedirect(reverse("mfa_index")) return view_func(request, *args, **kwargs) return _wrapper_view if function: return decorator(function) return decorator ================================================ FILE: allauth/mfa/internal/flows/trust.py ================================================ import time from dataclasses import dataclass from django.contrib.auth.models import AbstractBaseUser from django.core.signing import BadSignature, Signer from django.http import HttpRequest, HttpResponse from django.utils.crypto import salted_hmac from allauth.mfa import app_settings from allauth.mfa.models import Authenticator @dataclass class IssuedTrust: fingerprint: str at: int def create_config_fingerprint(user: AbstractBaseUser) -> str: """ If the user changes anything about his security setup, we want to invalidate any trust that was issued before. """ salt = "allauth.mfa.trust" parts: list[str] = [] parts.append(str(user.pk)) parts.append(user.password) for authenticator in Authenticator.objects.filter(user_id=user.pk).order_by("pk"): parts.append(str(authenticator.pk)) parts.append(str(authenticator.type)) seed = authenticator.data.get("seed") if seed is not None: parts.append(str(seed)) return salted_hmac(salt, "|".join(parts), algorithm="sha256").hexdigest() def decode_trust_cookie(request: HttpRequest) -> list[IssuedTrust]: value = request.COOKIES.get(app_settings.TRUST_COOKIE_NAME) if not value: return [] signer = Signer() try: data = signer.unsign_object(value) except BadSignature: return [] trusts = [IssuedTrust(fingerprint=entry[0], at=entry[1]) for entry in data] now = time.time() trusts = list( filter( lambda t: t.at + app_settings.TRUST_COOKIE_AGE.total_seconds() > now, trusts ) ) return trusts def encode_trust_cookie(trusts: list[IssuedTrust]) -> str: signer = Signer() return signer.sign_object([(it.fingerprint, it.at) for it in trusts]) def trust_browser( request: HttpRequest, user: AbstractBaseUser, response: HttpResponse ) -> None: fingerprint = create_config_fingerprint(user) trusts = decode_trust_cookie(request) trusts.append(IssuedTrust(fingerprint=fingerprint, at=int(time.time()))) response.set_cookie( app_settings.TRUST_COOKIE_NAME, encode_trust_cookie(trusts), max_age=app_settings.TRUST_COOKIE_AGE, path=app_settings.TRUST_COOKIE_PATH, domain=app_settings.TRUST_COOKIE_DOMAIN, secure=app_settings.TRUST_COOKIE_SECURE, httponly=app_settings.TRUST_COOKIE_HTTPONLY, samesite=app_settings.TRUST_COOKIE_SAMESITE, ) def is_trusted_browser(request: HttpRequest, user: AbstractBaseUser) -> bool: if not app_settings._TRUST_STAGE_ENABLED: return False trusts = decode_trust_cookie(request) fingerprint = create_config_fingerprint(user) return any([t.fingerprint == fingerprint for t in trusts]) ================================================ FILE: allauth/mfa/migrations/0001_initial.py ================================================ # Generated by Django 3.2.20 on 2023-08-19 14:43 import django.db.models.deletion from django.conf import settings from django.db import migrations, models class Migration(migrations.Migration): initial = True dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name="Authenticator", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ( "type", models.CharField( choices=[ ("recovery_codes", "Recovery codes"), ("totp", "TOTP Authenticator"), ], max_length=20, ), ), ("data", models.JSONField()), ( "user", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, ), ), ], options={ "unique_together": {("user", "type")}, }, ), ] ================================================ FILE: allauth/mfa/migrations/0002_authenticator_timestamps.py ================================================ # Generated by Django 3.2.22 on 2023-11-06 12:04 import django.utils.timezone from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("mfa", "0001_initial"), ] operations = [ migrations.AddField( model_name="authenticator", name="created_at", field=models.DateTimeField(default=django.utils.timezone.now), ), migrations.AddField( model_name="authenticator", name="last_used_at", field=models.DateTimeField(null=True), ), ] ================================================ FILE: allauth/mfa/migrations/0003_authenticator_type_uniq.py ================================================ # Generated by Django 3.2.20 on 2023-09-27 11:59 from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("mfa", "0002_authenticator_timestamps"), ] operations = [ migrations.AlterField( model_name="authenticator", name="type", field=models.CharField( choices=[ ("recovery_codes", "Recovery codes"), ("totp", "TOTP Authenticator"), ("webauthn", "WebAuthn"), ], max_length=20, ), ), migrations.AlterUniqueTogether( name="authenticator", unique_together=set(), ), migrations.AddConstraint( model_name="authenticator", constraint=models.UniqueConstraint( condition=models.Q(("type__in", ("totp", "recovery_codes"))), fields=("user", "type"), name="unique_authenticator_type", ), ), ] ================================================ FILE: allauth/mfa/migrations/__init__.py ================================================ ================================================ FILE: allauth/mfa/models.py ================================================ from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.db import models from django.db.models import Q from django.db.models.constraints import UniqueConstraint from django.utils import timezone from django.utils.translation import gettext_lazy as _ from allauth import app_settings as allauth_settings if not allauth_settings.MFA_ENABLED: raise ImproperlyConfigured( "allauth.mfa not installed, yet its models are imported." ) class AuthenticatorManager(models.Manager): pass class Authenticator(models.Model): class Type(models.TextChoices): RECOVERY_CODES = "recovery_codes", _("Recovery codes") TOTP = "totp", _("TOTP Authenticator") WEBAUTHN = "webauthn", _("WebAuthn") objects = AuthenticatorManager() user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) type = models.CharField(max_length=20, choices=Type.choices) data = models.JSONField() created_at = models.DateTimeField(default=timezone.now) last_used_at = models.DateTimeField(null=True) class Meta: constraints = [ UniqueConstraint( fields=["user", "type"], name="unique_authenticator_type", condition=Q( type__in=( "totp", "recovery_codes", ) ), ) ] def __str__(self) -> str: if self.type == self.Type.WEBAUTHN: return self.wrap().name return self.get_type_display() def wrap(self): from allauth.mfa.recovery_codes.internal.auth import RecoveryCodes from allauth.mfa.totp.internal.auth import TOTP from allauth.mfa.webauthn.internal.auth import WebAuthn return { self.Type.TOTP: TOTP, self.Type.RECOVERY_CODES: RecoveryCodes, self.Type.WEBAUTHN: WebAuthn, }[self.type](self) def record_usage(self) -> None: self.last_used_at = timezone.now() self.save(update_fields=["last_used_at"]) ================================================ FILE: allauth/mfa/recovery_codes/__init__.py ================================================ ================================================ FILE: allauth/mfa/recovery_codes/forms.py ================================================ from django import forms from allauth.mfa.adapter import get_adapter from allauth.mfa.recovery_codes.internal import flows class GenerateRecoveryCodesForm(forms.Form): def __init__(self, *args, **kwargs) -> None: self.user = kwargs.pop("user") super().__init__(*args, **kwargs) def clean(self): cleaned_data = super().clean() if not flows.can_generate_recovery_codes(self.user): raise get_adapter().validation_error("cannot_generate_recovery_codes") return cleaned_data ================================================ FILE: allauth/mfa/recovery_codes/internal/__init__.py ================================================ ================================================ FILE: allauth/mfa/recovery_codes/internal/auth.py ================================================ import hmac import secrets from hashlib import sha1 from allauth.mfa import app_settings from allauth.mfa.models import Authenticator from allauth.mfa.utils import decrypt, encrypt class RecoveryCodes: def __init__(self, instance: Authenticator) -> None: self.instance = instance @classmethod def activate(cls, user) -> "RecoveryCodes": instance = Authenticator.objects.filter( user=user, type=Authenticator.Type.RECOVERY_CODES ).first() if instance: return cls(instance) instance = Authenticator( user=user, type=Authenticator.Type.RECOVERY_CODES, data={ "seed": encrypt(cls.generate_seed()), "used_mask": 0, }, ) instance.save() return cls(instance) @classmethod def generate_seed(self) -> str: key = secrets.token_hex(40) return key def _get_migrated_codes(self) -> list[str] | None: codes = self.instance.data.get("migrated_codes") if codes is not None: return [decrypt(code) for code in codes] return None def generate_codes(self) -> list[str]: migrated_codes = self._get_migrated_codes() if migrated_codes is not None: return migrated_codes ret = [] seed = decrypt(self.instance.data["seed"]) h = hmac.new(key=seed.encode("ascii"), msg=None, digestmod=sha1) byte_count = min(app_settings.RECOVERY_CODE_DIGITS // 2, h.digest_size) for i in range(app_settings.RECOVERY_CODE_COUNT): h.update((f"{i:3},").encode()) value = int.from_bytes( h.digest()[:byte_count], byteorder="big", signed=False ) value %= 10**app_settings.RECOVERY_CODE_DIGITS fmt_value = str(value).zfill(app_settings.RECOVERY_CODE_DIGITS) ret.append(fmt_value) return ret def _is_code_used(self, i: int) -> bool: used_mask = self.instance.data["used_mask"] return bool(used_mask & (1 << i)) def _mark_code_used(self, i: int) -> None: used_mask = self.instance.data["used_mask"] used_mask |= 1 << i self.instance.data["used_mask"] = used_mask self.instance.save() def get_unused_codes(self) -> list[str]: migrated_codes = self._get_migrated_codes() if migrated_codes is not None: return migrated_codes ret = [] for i, code in enumerate(self.generate_codes()): if self._is_code_used(i): continue ret.append(code) return ret def _validate_migrated_code(self, code: str) -> bool | None: migrated_codes = self._get_migrated_codes() if migrated_codes is None: return None try: idx = migrated_codes.index(code) except ValueError: return False else: migrated_codes = self.instance.data["migrated_codes"] assert isinstance(migrated_codes, list) # nosec migrated_codes.pop(idx) self.instance.data["migrated_codes"] = migrated_codes self.instance.save() return True def validate_code(self, code: str) -> bool: ret = self._validate_migrated_code(code) if ret is not None: return ret for i, c in enumerate(self.generate_codes()): if self._is_code_used(i): continue if code == c: self._mark_code_used(i) return True return False ================================================ FILE: allauth/mfa/recovery_codes/internal/flows.py ================================================ from django.contrib import messages from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.internal.flows.reauthentication import ( raise_if_reauthentication_required, ) from allauth.mfa import app_settings, signals from allauth.mfa.models import Authenticator from allauth.mfa.recovery_codes.internal.auth import RecoveryCodes def can_generate_recovery_codes(user) -> bool: return ( Authenticator.objects.filter(user=user) .exclude(type=Authenticator.Type.RECOVERY_CODES) .exists() ) def generate_recovery_codes(request) -> Authenticator: raise_if_reauthentication_required(request) Authenticator.objects.filter( user=request.user, type=Authenticator.Type.RECOVERY_CODES ).delete() rc_auth = RecoveryCodes.activate(request.user) authenticator = rc_auth.instance adapter = get_account_adapter(request) adapter.add_message( request, messages.SUCCESS, "mfa/messages/recovery_codes_generated.txt" ) signals.authenticator_reset.send( sender=Authenticator, request=request, user=request.user, authenticator=authenticator, ) adapter.send_notification_mail("mfa/email/recovery_codes_generated", request.user) return authenticator def view_recovery_codes(request) -> Authenticator | None: authenticator = Authenticator.objects.filter( user=request.user, type=Authenticator.Type.RECOVERY_CODES, ).first() if not authenticator: return None raise_if_reauthentication_required(request) return authenticator def auto_generate_recovery_codes(request) -> Authenticator | None: """Automatically (implicitly) setup recovery codes when another authenticator is setup for. As this is part of setting up another (primary) authenticator, we do not send a notification email in this case. """ if Authenticator.Type.RECOVERY_CODES not in app_settings.SUPPORTED_TYPES: return None has_rc = Authenticator.objects.filter( user=request.user, type=Authenticator.Type.RECOVERY_CODES ).exists() if has_rc: return None rc = RecoveryCodes.activate(request.user) signals.authenticator_added.send( sender=Authenticator, request=request, user=request.user, authenticator=rc.instance, ) adapter = get_account_adapter(request) adapter.add_message( request, messages.SUCCESS, "mfa/messages/recovery_codes_generated.txt" ) return rc.instance ================================================ FILE: allauth/mfa/recovery_codes/urls.py ================================================ from django.urls import URLPattern, URLResolver, path from allauth.mfa.recovery_codes import views urlpatterns: list[URLPattern | URLResolver] = [ path("", views.view_recovery_codes, name="mfa_view_recovery_codes"), path( "generate/", views.generate_recovery_codes, name="mfa_generate_recovery_codes", ), path( "download/", views.download_recovery_codes, name="mfa_download_recovery_codes", ), ] ================================================ FILE: allauth/mfa/recovery_codes/views.py ================================================ from django.contrib.auth.decorators import login_required from django.http import Http404, HttpResponse, HttpResponseBase from django.urls import reverse_lazy from django.utils.decorators import method_decorator from django.views.decorators.cache import never_cache from django.views.generic import TemplateView from django.views.generic.edit import FormView from allauth.account import app_settings as account_settings from allauth.account.decorators import reauthentication_required from allauth.mfa import app_settings from allauth.mfa.models import Authenticator from allauth.mfa.recovery_codes.forms import GenerateRecoveryCodesForm from allauth.mfa.recovery_codes.internal import flows from allauth.utils import get_form_class @method_decorator(reauthentication_required, name="dispatch") class GenerateRecoveryCodesView(FormView): form_class = GenerateRecoveryCodesForm template_name = f"mfa/recovery_codes/generate.{account_settings.TEMPLATE_EXTENSION}" success_url = reverse_lazy("mfa_view_recovery_codes") def form_valid(self, form) -> HttpResponse: flows.generate_recovery_codes(self.request) return super().form_valid(form) def get_context_data(self, **kwargs): ret = super().get_context_data(**kwargs) unused_codes = [] authenticator = Authenticator.objects.filter( user=self.request.user, type=Authenticator.Type.RECOVERY_CODES ).first() if authenticator: unused_codes = authenticator.wrap().get_unused_codes() ret["unused_code_count"] = len(unused_codes) return ret def get_form_kwargs(self) -> dict: ret = super().get_form_kwargs() ret["user"] = self.request.user return ret def get_form_class(self): return get_form_class( app_settings.FORMS, "generate_recovery_codes", self.form_class ) generate_recovery_codes = GenerateRecoveryCodesView.as_view() @method_decorator(login_required, name="dispatch") @method_decorator(never_cache, name="dispatch") class DownloadRecoveryCodesView(TemplateView): template_name = "mfa/recovery_codes/download.txt" content_type = "text/plain" def dispatch(self, request, *args, **kwargs) -> HttpResponseBase: self.authenticator = flows.view_recovery_codes(self.request) if not self.authenticator: raise Http404() self.unused_codes = self.authenticator.wrap().get_unused_codes() if not self.unused_codes: raise Http404() return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) ret["unused_codes"] = self.unused_codes return ret def render_to_response(self, context, **response_kwargs): response = super().render_to_response(context, **response_kwargs) response["Content-Disposition"] = 'attachment; filename="recovery-codes.txt"' return response download_recovery_codes = DownloadRecoveryCodesView.as_view() @method_decorator(login_required, name="dispatch") class ViewRecoveryCodesView(TemplateView): template_name = f"mfa/recovery_codes/index.{account_settings.TEMPLATE_EXTENSION}" def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) authenticator = flows.view_recovery_codes(self.request) if not authenticator: raise Http404() ret.update( { "unused_codes": authenticator.wrap().get_unused_codes(), "total_count": app_settings.RECOVERY_CODE_COUNT, } ) return ret view_recovery_codes = ViewRecoveryCodesView.as_view() ================================================ FILE: allauth/mfa/signals.py ================================================ from django.dispatch import Signal from allauth.account import app_settings as account_settings from allauth.mfa import app_settings from allauth.mfa.adapter import get_adapter from allauth.mfa.utils import is_mfa_enabled # Emitted when an authenticator is added. # Arguments: request, user, authenticator authenticator_added = Signal() # Emitted when an authenticator is removed. # Arguments: request, user, authenticator authenticator_removed = Signal() # Emitted when an authenticator is reset (e.g. recovery codes regenerated). # Arguments: request, user, authenticator authenticator_reset = Signal() def on_add_email(sender, email, user, **kwargs): if app_settings.ALLOW_UNVERIFIED_EMAIL: return if account_settings.EMAIL_VERIFICATION_BY_CODE_ENABLED: return if is_mfa_enabled(user): adapter = get_adapter() raise adapter.validation_error("add_email_blocked") ================================================ FILE: allauth/mfa/stages.py ================================================ from allauth.account import app_settings as account_settings from allauth.account.internal.constants import LoginStageKey as AccountLoginStageKey from allauth.account.stages import LoginStage from allauth.core.internal.httpkit import headed_redirect_response, is_headless_request from allauth.mfa import app_settings from allauth.mfa.internal.constants import LoginStageKey from allauth.mfa.internal.flows import trust from allauth.mfa.models import Authenticator from allauth.mfa.utils import is_mfa_enabled from allauth.mfa.webauthn.internal.flows import did_use_passwordless_login class AuthenticateStage(LoginStage): key = LoginStageKey.MFA_AUTHENTICATE.value urlname = "mfa_authenticate" def handle(self): response, cont = None, True if self._should_handle(self.request): self.state["authentication_required"] = True response = headed_redirect_response("mfa_authenticate") return response, cont def _should_handle(self, request) -> bool: if self.login.user is None: return False if not is_mfa_enabled( self.login.user, [Authenticator.Type.TOTP, Authenticator.Type.WEBAUTHN] ): return False if did_use_passwordless_login(request): return False if trust.is_trusted_browser(request, self.login.user): return False return True class TrustStage(LoginStage): key = LoginStageKey.MFA_TRUST.value urlname = "mfa_trust" def handle(self): lbc_stage = self.controller.get_stage(AccountLoginStageKey.LOGIN_BY_CODE) auth_stage = self.controller.get_stage(AuthenticateStage.key) if ( not app_settings.TRUST_ENABLED or not auth_stage or not auth_stage.state.get("authentication_required") ) and ( not account_settings.LOGIN_BY_CODE_TRUST_ENABLED or not lbc_stage or not lbc_stage.state.get("login_by_code_required") ): return None, True client = is_headless_request(self.request) if client and client == "app": # Trust-this-browser relies on cookies. return None, True response = headed_redirect_response("mfa_trust") return response, True ================================================ FILE: allauth/mfa/static/mfa/js/webauthn-json.js ================================================ // https://github.com/github/webauthn-json 'use strict'; (() => { const __defProp = Object.defineProperty const __export = (target, all) => { for (const name in all) { __defProp(target, name, { get: all[name], enumerable: true }) } } const __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { const fulfilled = (value) => { try { step(generator.next(value)) } catch (e) { reject(e) } } const rejected = (value) => { try { step(generator.throw(value)) } catch (e) { reject(e) } } var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected) step((generator = generator.apply(__this, __arguments)).next()) }) } // src/webauthn-json/index.ts const webauthn_json_exports = {} __export(webauthn_json_exports, { create: () => create, get: () => get, schema: () => schema, supported: () => supported }) // src/webauthn-json/base64url.ts function base64urlToBuffer (baseurl64String) { const padding = '=='.slice(0, (4 - baseurl64String.length % 4) % 4) const base64String = baseurl64String.replace(/-/g, '+').replace(/_/g, '/') + padding const str = atob(base64String) const buffer = new ArrayBuffer(str.length) const byteView = new Uint8Array(buffer) for (let i = 0; i < str.length; i++) { byteView[i] = str.charCodeAt(i) } return buffer } function bufferToBase64url (buffer) { const byteView = new Uint8Array(buffer) let str = '' for (const charCode of byteView) { str += String.fromCharCode(charCode) } const base64String = btoa(str) const base64urlString = base64String.replace(/\+/g, '-').replace( /\//g, '_' ).replace(/=/g, '') return base64urlString } // src/webauthn-json/convert.ts const copyValue = 'copy' const convertValue = 'convert' function convert (conversionFn, schema2, input) { if (schema2 === copyValue) { return input } if (schema2 === convertValue) { return conversionFn(input) } if (schema2 instanceof Array) { return input.map((v) => convert(conversionFn, schema2[0], v)) } if (schema2 instanceof Object) { const output = {} for (const [key, schemaField] of Object.entries(schema2)) { if (schemaField.derive) { const v = schemaField.derive(input) if (v !== void 0) { input[key] = v } } if (!(key in input)) { if (schemaField.required) { throw new Error(`Missing key: ${key}`) } continue } if (input[key] == null) { output[key] = null continue } output[key] = convert( conversionFn, schemaField.schema, input[key] ) } return output } } function derived (schema2, derive) { return { required: true, schema: schema2, derive } } function required (schema2) { return { required: true, schema: schema2 } } function optional (schema2) { return { required: false, schema: schema2 } } // src/webauthn-json/basic/schema.ts const publicKeyCredentialDescriptorSchema = { type: required(copyValue), id: required(convertValue), transports: optional(copyValue) } const simplifiedExtensionsSchema = { appid: optional(copyValue), appidExclude: optional(copyValue), credProps: optional(copyValue) } const simplifiedClientExtensionResultsSchema = { appid: optional(copyValue), appidExclude: optional(copyValue), credProps: optional(copyValue) } const credentialCreationOptions = { publicKey: required({ rp: required(copyValue), user: required({ id: required(convertValue), name: required(copyValue), displayName: required(copyValue) }), challenge: required(convertValue), pubKeyCredParams: required(copyValue), timeout: optional(copyValue), excludeCredentials: optional([publicKeyCredentialDescriptorSchema]), authenticatorSelection: optional(copyValue), attestation: optional(copyValue), extensions: optional(simplifiedExtensionsSchema) }), signal: optional(copyValue) } const publicKeyCredentialWithAttestation = { type: required(copyValue), id: required(copyValue), rawId: required(convertValue), authenticatorAttachment: optional(copyValue), response: required({ clientDataJSON: required(convertValue), attestationObject: required(convertValue), transports: derived( copyValue, (response) => { let _a return ((_a = response.getTransports) == null ? void 0 : _a.call(response)) || [] } ) }), clientExtensionResults: derived( simplifiedClientExtensionResultsSchema, (pkc) => pkc.getClientExtensionResults() ) } const credentialRequestOptions = { mediation: optional(copyValue), publicKey: required({ challenge: required(convertValue), timeout: optional(copyValue), rpId: optional(copyValue), allowCredentials: optional([publicKeyCredentialDescriptorSchema]), userVerification: optional(copyValue), extensions: optional(simplifiedExtensionsSchema) }), signal: optional(copyValue) } const publicKeyCredentialWithAssertion = { type: required(copyValue), id: required(copyValue), rawId: required(convertValue), authenticatorAttachment: optional(copyValue), response: required({ clientDataJSON: required(convertValue), authenticatorData: required(convertValue), signature: required(convertValue), userHandle: required(convertValue) }), clientExtensionResults: derived( simplifiedClientExtensionResultsSchema, (pkc) => pkc.getClientExtensionResults() ) } var schema = { credentialCreationOptions, publicKeyCredentialWithAttestation, credentialRequestOptions, publicKeyCredentialWithAssertion } // src/webauthn-json/basic/api.ts function createRequestFromJSON (requestJSON) { return convert(base64urlToBuffer, credentialCreationOptions, requestJSON) } function createResponseToJSON (credential) { return convert( bufferToBase64url, publicKeyCredentialWithAttestation, credential ) } function create (requestJSON) { return __async(this, null, function * () { const credential = yield navigator.credentials.create( createRequestFromJSON(requestJSON) ) return createResponseToJSON(credential) }) } function getRequestFromJSON (requestJSON) { return convert(base64urlToBuffer, credentialRequestOptions, requestJSON) } function getResponseToJSON (credential) { return convert( bufferToBase64url, publicKeyCredentialWithAssertion, credential ) } function get (requestJSON) { return __async(this, null, function * () { const credential = yield navigator.credentials.get( getRequestFromJSON(requestJSON) ) return getResponseToJSON(credential) }) } // src/webauthn-json/basic/supported.ts function supported () { return !!(navigator.credentials && navigator.credentials.create && navigator.credentials.get && window.PublicKeyCredential) } // src/webauthn-json/browser-global.ts globalThis.webauthnJSON = webauthn_json_exports })() // # sourceMappingURL=webauthn-json.browser-global.js.map ================================================ FILE: allauth/mfa/static/mfa/js/webauthn.js ================================================ (function () { const allauth = window.allauth = window.allauth || {} const webauthnJSON = window.webauthnJSON function dispatchError (exception) { const event = new CustomEvent('allauth.error', { detail: { tags: ['mfa', 'webauthn'], exception }, cancelable: true }) document.dispatchEvent(event) if (!event.defaultPrevented) { console.error(exception) } } async function createCredentials (credentials, passwordless) { credentials = JSON.parse(JSON.stringify(credentials)) const sel = credentials.publicKey.authenticatorSelection if (passwordless != null) { sel.residentKey = passwordless ? 'required' : 'discouraged' sel.requireResidentKey = passwordless sel.userVerification = passwordless ? 'required' : 'discouraged' } return await webauthnJSON.create(credentials) } function signupForm (o) { const signupBtn = document.getElementById(o.ids.signup) return addOrSignupForm(o, signupBtn, null) } function addForm (o) { const addBtn = document.getElementById(o.ids.add) const passwordlessCb = o.ids.passwordless ? document.getElementById(o.ids.passwordless) : null const passwordlessFn = () => passwordlessCb ? passwordlessCb.checked : false return addOrSignupForm(o, addBtn, passwordlessFn) } function getData (o) { if (typeof o.ids.data !== 'undefined') { return JSON.parse(document.getElementById(o.ids.data).textContent) } return o.data } function addOrSignupForm (o, actionBtn, passwordlessFn) { const credentialInput = document.getElementById(o.ids.credential) const form = credentialInput.closest('form') actionBtn.addEventListener('click', async function () { const passwordless = passwordlessFn ? passwordlessFn() : undefined try { const credential = await createCredentials(getData(o).creation_options, passwordless) credentialInput.value = JSON.stringify(credential) form.submit() } catch (e) { dispatchError(e) } }) } function loginForm (o) { const loginBtn = document.getElementById(o.ids.login) const form = loginBtn.form const credentialInput = document.getElementById(o.ids.credential) loginBtn.addEventListener('click', async function (e) { e.preventDefault() try { const response = await fetch(form.action, { method: 'GET', headers: { Accept: 'application/json' } }) if (!response.ok) { throw new Error('Unable to fetch passkey data from server.') } const data = await response.json() const credential = await webauthnJSON.get(data.request_options) credentialInput.value = JSON.stringify(credential) form.submit() } catch (e) { dispatchError(e) } }) } function authenticateForm (o) { const authenticateBtn = document.getElementById(o.ids.authenticate) const credentialInput = document.getElementById(o.ids.credential) const form = credentialInput.closest('form') authenticateBtn.addEventListener('click', async function (e) { e.preventDefault() try { const credential = await webauthnJSON.get(getData(o).request_options) credentialInput.value = JSON.stringify(credential) form.submit() } catch (e) { dispatchError(e) } }) } allauth.webauthn = { forms: { addForm, authenticateForm, loginForm, signupForm } } })() ================================================ FILE: allauth/mfa/totp/__init__.py ================================================ ================================================ FILE: allauth/mfa/totp/forms.py ================================================ from django import forms from django.utils.translation import gettext_lazy as _ from allauth.mfa.adapter import get_adapter from allauth.mfa.internal.flows.add import validate_can_add_authenticator from allauth.mfa.totp.internal import auth class ActivateTOTPForm(forms.Form): code = forms.CharField( label=_("Authenticator code"), widget=forms.TextInput( attrs={"placeholder": _("Code"), "autocomplete": "one-time-code"}, ), ) def __init__(self, *args, **kwargs) -> None: self.user = kwargs.pop("user") super().__init__(*args, **kwargs) self.secret = auth.get_totp_secret(regenerate=not self.is_bound) def clean_code(self) -> str: validate_can_add_authenticator(self.user) code = self.cleaned_data["code"] if not auth.validate_totp_code(self.secret, code): raise get_adapter().validation_error("incorrect_code") return code class DeactivateTOTPForm(forms.Form): def __init__(self, *args, **kwargs) -> None: self.authenticator = kwargs.pop("authenticator") super().__init__(*args, **kwargs) def clean(self): cleaned_data = super().clean() adapter = get_adapter() if not adapter.can_delete_authenticator(self.authenticator): raise adapter.validation_error("cannot_delete_authenticator") return cleaned_data ================================================ FILE: allauth/mfa/totp/internal/__init__.py ================================================ ================================================ FILE: allauth/mfa/totp/internal/auth.py ================================================ import base64 import hashlib import hmac import secrets import struct import time from collections.abc import Iterator from django.core.cache import cache from allauth.core import context from allauth.mfa import app_settings from allauth.mfa.models import Authenticator from allauth.mfa.utils import decrypt, encrypt SECRET_SESSION_KEY = "mfa.totp.secret" # nosec def generate_totp_secret(length: int = 20) -> str: random_bytes = secrets.token_bytes(length) return base64.b32encode(random_bytes).decode("utf-8") def get_totp_secret(regenerate: bool = False) -> str: secret = None if not regenerate: secret = context.request.session.get(SECRET_SESSION_KEY) if not secret: secret = context.request.session[SECRET_SESSION_KEY] = generate_totp_secret() return secret def yield_hotp_counters_from_time() -> Iterator[int]: current_time = int(time.time()) # Get the current Unix timestamp counter = current_time // app_settings.TOTP_PERIOD for i in range(-app_settings.TOTP_TOLERANCE, app_settings.TOTP_TOLERANCE + 1): yield counter + i def hotp_value(secret: str, counter: int) -> int: # Convert the counter to a byte array using big-endian encoding counter_bytes = struct.pack(">Q", counter) secret_enc = base64.b32decode(secret.encode("ascii"), casefold=True) # Calculate the HMAC-SHA1 hash using the secret and counter hmac_result = hmac.new(secret_enc, counter_bytes, hashlib.sha1).digest() # Get the last 4 bits of the HMAC result to determine the offset offset = hmac_result[-1] & 0x0F # Extract an 31-bit slice from the HMAC result starting at the offset + 1 bit truncated_hash = bytearray(hmac_result[offset : offset + 4]) truncated_hash[0] = truncated_hash[0] & 0x7F # Convert the truncated hash to an integer value value = struct.unpack(">I", truncated_hash)[0] # Apply modulo to get a value within the specified number of digits value %= 10**app_settings.TOTP_DIGITS return value def format_hotp_value(value: int) -> str: return f"{value:0{app_settings.TOTP_DIGITS}}" def _is_insecure_bypass(code: str) -> bool: return bool(code and app_settings.TOTP_INSECURE_BYPASS_CODE == code) def validate_totp_code(secret: str, code: str) -> bool: if _is_insecure_bypass(code): return True counters = yield_hotp_counters_from_time() for counter in counters: value = hotp_value(secret, counter) if code == format_hotp_value(value): return True return False class TOTP: def __init__(self, instance: Authenticator) -> None: self.instance = instance @classmethod def activate(cls, user, secret: str) -> "TOTP": instance = Authenticator( user=user, type=Authenticator.Type.TOTP, data={"secret": encrypt(secret)} ) instance.save() return cls(instance) def validate_code(self, code: str) -> bool: if _is_insecure_bypass(code): return True if self._is_code_used(code): return False secret = decrypt(self.instance.data["secret"]) valid = validate_totp_code(secret, code) if valid: self._mark_code_used(code) return valid def _get_used_cache_key(self, code: str) -> str: return f"allauth.mfa.totp.used?user={self.instance.user_id}&code={code}" def _is_code_used(self, code: str) -> bool: return cache.get(self._get_used_cache_key(code)) == "y" def _mark_code_used(self, code: str) -> None: cache.set(self._get_used_cache_key(code), "y", timeout=app_settings.TOTP_PERIOD) ================================================ FILE: allauth/mfa/totp/internal/flows.py ================================================ from django.contrib import messages from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.internal.flows.reauthentication import ( raise_if_reauthentication_required, ) from allauth.mfa import signals from allauth.mfa.base.internal.flows import delete_and_cleanup from allauth.mfa.models import Authenticator from allauth.mfa.recovery_codes.internal.flows import auto_generate_recovery_codes from allauth.mfa.totp.internal.auth import TOTP def activate_totp(request, form) -> tuple[Authenticator, Authenticator | None]: raise_if_reauthentication_required(request) totp_auth = TOTP.activate(request.user, form.secret).instance signals.authenticator_added.send( sender=Authenticator, request=request, user=request.user, authenticator=totp_auth, ) adapter = get_account_adapter(request) adapter.add_message(request, messages.SUCCESS, "mfa/messages/totp_activated.txt") adapter.send_notification_mail("mfa/email/totp_activated", request.user) rc_auth = auto_generate_recovery_codes(request) return totp_auth, rc_auth def deactivate_totp(request, authenticator: Authenticator) -> None: raise_if_reauthentication_required(request) delete_and_cleanup(request, authenticator) adapter = get_account_adapter(request) adapter.add_message(request, messages.SUCCESS, "mfa/messages/totp_deactivated.txt") adapter.send_notification_mail("mfa/email/totp_deactivated", request.user) ================================================ FILE: allauth/mfa/totp/urls.py ================================================ from django.urls import URLPattern, URLResolver, path from allauth.mfa.totp import views urlpatterns: list[URLPattern | URLResolver] = [ path("activate/", views.activate_totp, name="mfa_activate_totp"), path("deactivate/", views.deactivate_totp, name="mfa_deactivate_totp"), ] ================================================ FILE: allauth/mfa/totp/views.py ================================================ import base64 from django.contrib.auth.decorators import login_required from django.http import HttpResponse, HttpResponseBase, HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.urls import reverse, reverse_lazy from django.utils.decorators import method_decorator from django.views.generic.edit import FormView from allauth.account import app_settings as account_settings from allauth.account.decorators import reauthentication_required from allauth.mfa import app_settings from allauth.mfa.adapter import get_adapter from allauth.mfa.internal.flows.add import redirect_if_add_not_allowed from allauth.mfa.models import Authenticator from allauth.mfa.totp.forms import ActivateTOTPForm, DeactivateTOTPForm from allauth.mfa.totp.internal import flows from allauth.mfa.utils import is_mfa_enabled from allauth.utils import get_form_class @method_decorator(redirect_if_add_not_allowed, name="dispatch") @method_decorator(reauthentication_required, name="dispatch") class ActivateTOTPView(FormView): form_class = ActivateTOTPForm template_name = f"mfa/totp/activate_form.{account_settings.TEMPLATE_EXTENSION}" def dispatch(self, request, *args, **kwargs) -> HttpResponseBase: if is_mfa_enabled(request.user, [Authenticator.Type.TOTP]): return HttpResponseRedirect(reverse("mfa_deactivate_totp")) return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) adapter = get_adapter() totp_url = adapter.build_totp_url( self.request.user, ret["form"].secret, ) totp_svg = adapter.build_totp_svg(totp_url) base64_data = base64.b64encode(totp_svg.encode("utf8")).decode("utf-8") totp_data_uri = f"data:image/svg+xml;base64,{base64_data}" ret.update( { "totp_svg": totp_svg, "totp_svg_data_uri": totp_data_uri, "totp_url": totp_url, } ) return ret def get_form_kwargs(self) -> dict: ret = super().get_form_kwargs() ret["user"] = self.request.user return ret def get_form_class(self): return get_form_class(app_settings.FORMS, "activate_totp", self.form_class) def get_success_url(self) -> str: if self.did_generate_recovery_codes: return reverse("mfa_view_recovery_codes") return reverse("mfa_index") def form_valid(self, form) -> HttpResponse: totp_auth, rc_auth = flows.activate_totp(self.request, form) self.did_generate_recovery_codes = bool(rc_auth) return super().form_valid(form) activate_totp = ActivateTOTPView.as_view() @method_decorator(login_required, name="dispatch") class DeactivateTOTPView(FormView): form_class = DeactivateTOTPForm template_name = f"mfa/totp/deactivate_form.{account_settings.TEMPLATE_EXTENSION}" success_url = reverse_lazy("mfa_index") def dispatch(self, request, *args, **kwargs) -> HttpResponseBase: self.authenticator = get_object_or_404( Authenticator, user=self.request.user, type=Authenticator.Type.TOTP, ) if not is_mfa_enabled(request.user, [Authenticator.Type.TOTP]): return HttpResponseRedirect(reverse("mfa_activate_totp")) return self._dispatch(request, *args, **kwargs) @method_decorator(reauthentication_required) def _dispatch(self, request, *args, **kwargs): """There's no point to reauthenticate when MFA is not enabled, so the `is_mfa_enabled` check needs to go first, which is why we cannot slap a `reauthentication_required` decorator on the `dispatch` directly. """ return super().dispatch(request, *args, **kwargs) def get_form_kwargs(self) -> dict: ret = super().get_form_kwargs() ret["authenticator"] = self.authenticator # The deactivation form does not require input, yet, can generate # validation errors in case deactivation is not allowed. We want to # immediately present such errors even before the user actually posts # the form, which is why we put an empty data payload in here. ret.setdefault("data", {}) return ret def get_form_class(self): return get_form_class(app_settings.FORMS, "deactivate_totp", self.form_class) def form_valid(self, form) -> HttpResponse: flows.deactivate_totp(self.request, self.authenticator) return super().form_valid(form) deactivate_totp = DeactivateTOTPView.as_view() ================================================ FILE: allauth/mfa/urls.py ================================================ from django.urls import URLPattern, URLResolver, include, path from allauth.mfa import app_settings urlpatterns: list[URLPattern | URLResolver] = [ path("", include("allauth.mfa.base.urls")) ] if "totp" in app_settings.SUPPORTED_TYPES: urlpatterns.append(path("totp/", include("allauth.mfa.totp.urls"))) if "recovery_codes" in app_settings.SUPPORTED_TYPES: urlpatterns.append( path("recovery-codes/", include("allauth.mfa.recovery_codes.urls")) ) if "webauthn" in app_settings.SUPPORTED_TYPES: urlpatterns.append(path("webauthn/", include("allauth.mfa.webauthn.urls"))) ================================================ FILE: allauth/mfa/utils.py ================================================ from allauth.mfa.adapter import get_adapter def encrypt(text: str) -> str: return get_adapter().encrypt(text) def decrypt(encrypted_text: str) -> str: return get_adapter().decrypt(encrypted_text) def is_mfa_enabled(user, types=None) -> bool: return get_adapter().is_mfa_enabled(user, types=types) ================================================ FILE: allauth/mfa/webauthn/__init__.py ================================================ ================================================ FILE: allauth/mfa/webauthn/forms.py ================================================ from django import forms from django.utils.translation import gettext_lazy as _ from allauth.core import context from allauth.mfa import app_settings from allauth.mfa.adapter import get_adapter from allauth.mfa.base.internal.flows import check_rate_limit, post_authentication from allauth.mfa.models import Authenticator from allauth.mfa.webauthn.internal import auth, flows class _BaseAddWebAuthnForm(forms.Form): name = forms.CharField(required=False) credential = forms.JSONField(required=True, widget=forms.HiddenInput) def __init__(self, *args, **kwargs) -> None: self.user = kwargs.pop("user") initial = kwargs.setdefault("initial", {}) initial.setdefault( "name", get_adapter().generate_authenticator_name( self.user, Authenticator.Type.WEBAUTHN ), ) super().__init__(*args, **kwargs) def clean_name(self) -> str: """ We don't want to make `name` a required field, as the WebAuthn ceremony happens before posting the resulting credential, and we don't want to reject a valid credential because of a missing name -- it might be resident already. So, gracefully plug in a name. """ name = self.cleaned_data["name"] if not name: name = get_adapter().generate_authenticator_name( self.user, Authenticator.Type.WEBAUTHN ) return name def clean(self): cleaned_data = super().clean() credential = cleaned_data.get("credential") if credential: # Explicitly parse JSON payload -- otherwise, register_complete() # crashes with some random TypeError and we don't want to do # Pokemon-style exception handling. auth.parse_registration_response(credential) auth.complete_registration(credential) return cleaned_data class AddWebAuthnForm(_BaseAddWebAuthnForm): if app_settings.PASSKEY_LOGIN_ENABLED: passwordless = forms.BooleanField( label=_("Passwordless"), required=False, help_text=_( "Enabling passwordless operation allows you to sign in using just this key, but imposes additional requirements such as biometrics or PIN protection." ), ) class SignupWebAuthnForm(_BaseAddWebAuthnForm): pass class AuthenticateWebAuthnForm(forms.Form): credential = forms.JSONField(required=True, widget=forms.HiddenInput) reauthenticated = False passwordless = False def __init__(self, *args, **kwargs) -> None: self.user = kwargs.pop("user") super().__init__(*args, **kwargs) def clean_credential(self) -> Authenticator: credential = self.cleaned_data["credential"] # Explicitly parse JSON payload -- otherwise, authenticate_complete() # crashes with some random TypeError and we don't want to do # Pokemon-style exception handling. auth.parse_authentication_response(credential) user = self.user if user is None: user = auth.extract_user_from_response(credential) clear_rl = check_rate_limit(user) authenticator = auth.complete_authentication(user, credential) clear_rl() return authenticator def save(self) -> None: authenticator = self.cleaned_data["credential"] post_authentication( context.request, authenticator, reauthenticated=self.reauthenticated, passwordless=self.passwordless, ) class LoginWebAuthnForm(AuthenticateWebAuthnForm): reauthenticated = False passwordless = True def __init__(self, *args, **kwargs) -> None: super().__init__(*args, user=None, **kwargs) class ReauthenticateWebAuthnForm(AuthenticateWebAuthnForm): reauthenticated = True passwordless = False class EditWebAuthnForm(forms.Form): name = forms.CharField(required=True) def __init__(self, *args, **kwargs) -> None: self.instance = kwargs.pop("instance") initial = kwargs.setdefault("initial", {}) initial.setdefault("name", self.instance.wrap().name) super().__init__(*args, **kwargs) def save(self) -> Authenticator: flows.rename_authenticator( context.request, self.instance, self.cleaned_data["name"] ) return self.instance ================================================ FILE: allauth/mfa/webauthn/internal/__init__.py ================================================ ================================================ FILE: allauth/mfa/webauthn/internal/auth.py ================================================ from typing import Any from django.contrib.auth import get_user_model import fido2.features from fido2.server import Fido2Server from fido2.utils import websafe_decode from fido2.webauthn import ( AttestedCredentialData, AuthenticationResponse, AuthenticatorData, PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity, RegistrationResponse, ResidentKeyRequirement, UserVerificationRequirement, ) from allauth.account.utils import url_str_to_user_pk from allauth.core import context from allauth.mfa import app_settings from allauth.mfa.adapter import get_adapter from allauth.mfa.models import Authenticator try: fido2.features.webauthn_json_mapping.enabled = True # type:ignore[attr-defined] except AttributeError: # https://github.com/Yubico/python-fido2/blob/main/doc/Migration_1-2.adoc pass STATE_SESSION_KEY = "mfa.webauthn.state" EXTENSIONS = {"credProps": True} def build_user_payload(user) -> PublicKeyCredentialUserEntity: kwargs = get_adapter().get_public_key_credential_user_entity(user) return PublicKeyCredentialUserEntity(**kwargs) def get_state() -> dict | None: return context.request.session.get(STATE_SESSION_KEY) def set_state(state: dict) -> None: context.request.session[STATE_SESSION_KEY] = state def clear_state() -> None: context.request.session.pop(STATE_SESSION_KEY, None) def get_server() -> Fido2Server: rp_kwargs = get_adapter().get_public_key_credential_rp_entity() rp = PublicKeyCredentialRpEntity(**rp_kwargs) verify_origin = None if app_settings.WEBAUTHN_ALLOW_INSECURE_ORIGIN: verify_origin = lambda o: True # noqa server = Fido2Server(rp, verify_origin=verify_origin) return server def parse_registration_response(response: Any) -> RegistrationResponse: try: return RegistrationResponse.from_dict(response) except TypeError: raise get_adapter().validation_error("incorrect_code") def begin_registration(user, passwordless: bool) -> dict: server = get_server() credentials = get_credentials(user) registration_data, state = server.register_begin( user=build_user_payload(user), credentials=credentials, resident_key_requirement=( ResidentKeyRequirement.REQUIRED if passwordless else ResidentKeyRequirement.DISCOURAGED ), user_verification=( UserVerificationRequirement.REQUIRED if passwordless else UserVerificationRequirement.DISCOURAGED ), extensions=EXTENSIONS, ) set_state(state) return dict(registration_data) def complete_registration(credential: dict) -> AuthenticatorData: server = get_server() state = get_state() if not state: raise get_adapter().validation_error("incorrect_code") try: binding = server.register_complete(state, credential) except ValueError: # raise ValueError("Wrong challenge in response.") raise get_adapter().validation_error("incorrect_code") clear_state() return binding def get_credentials(user) -> list[AttestedCredentialData]: credentials: list[AttestedCredentialData] = [] authenticators = Authenticator.objects.filter( user=user, type=Authenticator.Type.WEBAUTHN ) for authenticator in authenticators: credential_data = authenticator.wrap().authenticator_data.credential_data if credential_data: credentials.append(authenticator.wrap().authenticator_data.credential_data) return credentials def get_authenticator_by_credential_id( user, credential_id: bytes ) -> Authenticator | None: authenticators = Authenticator.objects.filter( user=user, type=Authenticator.Type.WEBAUTHN ) for authenticator in authenticators: if ( credential_id == authenticator.wrap().authenticator_data.credential_data.credential_id ): return authenticator return None def parse_authentication_response(response: Any) -> AuthenticationResponse: try: return AuthenticationResponse.from_dict(response) except (TypeError, ValueError): raise get_adapter().validation_error("incorrect_code") def begin_authentication(user=None) -> dict: server = get_server() request_options, state = server.authenticate_begin( credentials=get_credentials(user) if user else [], user_verification=UserVerificationRequirement.PREFERRED, ) set_state(state) return dict(request_options) def extract_user_from_response(response: dict): try: user_handle = response.get("response", {}).get("userHandle") user_pk = url_str_to_user_pk(websafe_decode(user_handle).decode("utf8")) except (ValueError, TypeError, KeyError): raise get_adapter().validation_error("incorrect_code") user = get_user_model().objects.filter(pk=user_pk).first() if not user: raise get_adapter().validation_error("incorrect_code") return user def complete_authentication(user, response: dict) -> Authenticator: credentials = get_credentials(user) server = get_server() state = get_state() if not state: raise get_adapter().validation_error("incorrect_code") try: binding = server.authenticate_complete(state, credentials, response) except ValueError as e: # ValueError: Unknown credential ID. raise get_adapter().validation_error("incorrect_code") from e clear_state() authenticator = get_authenticator_by_credential_id(user, binding.credential_id) if not authenticator: raise get_adapter().validation_error("incorrect_code") return authenticator class WebAuthn: def __init__(self, instance): self.instance = instance @classmethod def add(cls, user, name: str, credential: dict) -> "WebAuthn": instance = Authenticator( user=user, type=Authenticator.Type.WEBAUTHN, data={ "name": name, "credential": credential, }, ) instance.save() return cls(instance) @property def name(self) -> str: return self.instance.data["name"] @name.setter def name(self, name: str) -> None: self.instance.data["name"] = name @property def authenticator_data(self) -> AuthenticatorData: return parse_registration_response( self.instance.data["credential"] ).response.attestation_object.auth_data @property def is_passwordless(self) -> bool | None: return ( self.instance.data.get("credential", {}) .get("clientExtensionResults", {}) .get("credProps", {}) .get("rk") ) ================================================ FILE: allauth/mfa/webauthn/internal/flows.py ================================================ from collections.abc import Iterable from django.contrib import messages from django.http import HttpRequest from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.authentication import get_authentication_records from allauth.account.internal import flows from allauth.account.internal.flows.reauthentication import ( raise_if_reauthentication_required, ) from allauth.account.models import Login from allauth.mfa import signals from allauth.mfa.base.internal.flows import delete_and_cleanup, post_authentication from allauth.mfa.models import Authenticator from allauth.mfa.recovery_codes.internal.flows import auto_generate_recovery_codes from allauth.mfa.webauthn.internal import auth def begin_registration( request: HttpRequest, user, passwordless: bool, signup: bool = False ) -> dict: if not signup: raise_if_reauthentication_required(request) creation_options = auth.begin_registration(user, passwordless) return creation_options def signup_authenticator(request, user, name: str, credential: dict) -> Authenticator: authenticator, rc_authenticator = _signup_or_add_authenticator( request, user, name, credential, signup=True ) return authenticator def add_authenticator( request, name: str, credential: dict ) -> tuple[Authenticator, Authenticator | None]: raise_if_reauthentication_required(request) return _signup_or_add_authenticator( request, user=request.user, name=name, credential=credential, signup=False, ) def _signup_or_add_authenticator( request, user, name: str, credential: dict, signup: bool = False, ) -> tuple[Authenticator, Authenticator | None]: authenticator = auth.WebAuthn.add( user, name, credential, ).instance signals.authenticator_added.send( sender=Authenticator, request=request, user=user, authenticator=authenticator, ) adapter = get_account_adapter(request) adapter.add_message(request, messages.SUCCESS, "mfa/messages/webauthn_added.txt") if not signup: adapter.send_notification_mail("mfa/email/webauthn_added", user) rc_authenticator = None if not signup: rc_authenticator = auto_generate_recovery_codes(request) return authenticator, rc_authenticator def remove_authenticators(request, authenticators: Iterable[Authenticator]) -> None: raise_if_reauthentication_required(request) for authenticator in authenticators: remove_authenticator(request, authenticator) def remove_authenticator(request, authenticator: Authenticator) -> None: raise_if_reauthentication_required(request) delete_and_cleanup(request, authenticator) adapter = get_account_adapter(request) adapter.add_message(request, messages.SUCCESS, "mfa/messages/webauthn_removed.txt") adapter.send_notification_mail("mfa/email/webauthn_removed", request.user) def perform_passwordless_login(request, authenticator: Authenticator, login: Login): post_authentication(request, authenticator, passwordless=True) return flows.login.perform_login(request, login) def did_use_passwordless_login(request: HttpRequest) -> bool: records = get_authentication_records(request) return any( (record.get("method"), record.get("type"), record.get("passwordless")) == ("mfa", "webauthn", True) for record in records ) def reauthenticate(request: HttpRequest, authenticator: Authenticator) -> None: post_authentication(request, authenticator, reauthenticated=True) def rename_authenticator(request, authenticator: Authenticator, name: str) -> None: raise_if_reauthentication_required(request) wrapper = authenticator.wrap() wrapper.name = name authenticator.save() ================================================ FILE: allauth/mfa/webauthn/stages.py ================================================ from allauth.account.stages import LoginStage from allauth.core.internal.httpkit import headed_redirect_response from allauth.mfa.internal.constants import LoginStageKey class PasskeySignupStage(LoginStage): key = LoginStageKey.MFA_SIGNUP_WEBAUTHN.value urlname = "mfa_signup_webauthn" def handle(self): response, cont = None, True if self.login.state.get("passkey_signup"): response = headed_redirect_response("mfa_signup_webauthn") return response, cont ================================================ FILE: allauth/mfa/webauthn/urls.py ================================================ from django.urls import URLPattern, URLResolver, include, path from allauth.mfa import app_settings from allauth.mfa.webauthn import views urlpatterns: list[URLPattern | URLResolver] = [ path("", views.list_webauthn, name="mfa_list_webauthn"), path("add/", views.add_webauthn, name="mfa_add_webauthn"), path( "reauthenticate/", views.reauthenticate_webauthn, name="mfa_reauthenticate_webauthn", ), path( "keys//", include( [ path( "remove/", views.remove_webauthn, name="mfa_remove_webauthn", ), path( "edit/", views.edit_webauthn, name="mfa_edit_webauthn", ), ] ), ), ] if app_settings.PASSKEY_LOGIN_ENABLED: urlpatterns.append(path("login/", views.login_webauthn, name="mfa_login_webauthn")) if app_settings.PASSKEY_SIGNUP_ENABLED: urlpatterns.append( path("signup/", views.signup_webauthn, name="mfa_signup_webauthn") ) ================================================ FILE: allauth/mfa/webauthn/views.py ================================================ from django.contrib import messages from django.contrib.auth.decorators import login_required from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.urls import reverse, reverse_lazy from django.utils.decorators import method_decorator from django.views.generic.edit import DeleteView, FormView, UpdateView from django.views.generic.list import ListView from allauth.account import app_settings as account_settings from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.decorators import reauthentication_required from allauth.account.internal.decorators import login_stage_required from allauth.account.mixins import NextRedirectMixin, RedirectAuthenticatedUserMixin from allauth.account.models import Login from allauth.account.views import BaseReauthenticateView from allauth.mfa import app_settings from allauth.mfa.internal.flows.add import redirect_if_add_not_allowed from allauth.mfa.models import Authenticator from allauth.mfa.webauthn.forms import ( AddWebAuthnForm, EditWebAuthnForm, LoginWebAuthnForm, ReauthenticateWebAuthnForm, SignupWebAuthnForm, ) from allauth.mfa.webauthn.internal import auth, flows from allauth.mfa.webauthn.stages import PasskeySignupStage from allauth.utils import get_form_class @method_decorator(redirect_if_add_not_allowed, name="dispatch") @method_decorator(reauthentication_required, name="dispatch") class AddWebAuthnView(FormView): form_class = AddWebAuthnForm template_name = f"mfa/webauthn/add_form.{account_settings.TEMPLATE_EXTENSION}" def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data() creation_options = auth.begin_registration(self.request.user, False) ret["js_data"] = {"creation_options": creation_options} return ret def get_form_class(self): return get_form_class(app_settings.FORMS, "add_webauthn", self.form_class) def get_form_kwargs(self) -> dict: ret = super().get_form_kwargs() ret["user"] = self.request.user return ret def get_success_url(self) -> str: if self.did_generate_recovery_codes: return reverse("mfa_view_recovery_codes") return reverse("mfa_index") def form_valid(self, form) -> HttpResponse: auth, rc_auth = flows.add_authenticator( self.request, name=form.cleaned_data["name"], credential=form.cleaned_data["credential"], ) self.did_generate_recovery_codes = bool(rc_auth) return super().form_valid(form) add_webauthn = AddWebAuthnView.as_view() @method_decorator(login_required, name="dispatch") class ListWebAuthnView(ListView): template_name = ( f"mfa/webauthn/authenticator_list.{account_settings.TEMPLATE_EXTENSION}" ) context_object_name = "authenticators" def get_queryset(self): return Authenticator.objects.filter( user=self.request.user, type=Authenticator.Type.WEBAUTHN ) list_webauthn = ListWebAuthnView.as_view() @method_decorator(reauthentication_required, name="dispatch") class RemoveWebAuthnView(NextRedirectMixin, DeleteView): object: Authenticator # https://github.com/typeddjango/django-stubs/issues/1227 template_name = ( "mfa/webauthn/authenticator_confirm_delete." + account_settings.TEMPLATE_EXTENSION ) success_url = reverse_lazy("mfa_list_webauthn") def get_queryset(self): return Authenticator.objects.filter( user=self.request.user, type=Authenticator.Type.WEBAUTHN ) def form_valid(self, form) -> HttpResponse: authenticator = self.get_object() flows.remove_authenticator(self.request, authenticator) return HttpResponseRedirect(self.get_success_url()) remove_webauthn = RemoveWebAuthnView.as_view() class LoginWebAuthnView(RedirectAuthenticatedUserMixin, FormView): form_class = LoginWebAuthnForm def get(self, request, *args, **kwargs) -> HttpResponse: if get_account_adapter().is_ajax(request): request_options = auth.begin_authentication(user=None) data = {"request_options": request_options} return JsonResponse(data) return HttpResponseRedirect(reverse("account_login")) def get_form_class(self): return get_form_class(app_settings.FORMS, "login_webauthn", self.form_class) def form_invalid(self, form) -> HttpResponse: for message in form.errors.get("credential", []): get_account_adapter().add_message( self.request, messages.ERROR, message=message ) return HttpResponseRedirect(reverse("account_login")) def form_valid(self, form) -> HttpResponse: authenticator = form.cleaned_data["credential"] redirect_url = None login = Login(user=authenticator.user, redirect_url=redirect_url) return flows.perform_passwordless_login(self.request, authenticator, login) login_webauthn = LoginWebAuthnView.as_view() @method_decorator(login_required, name="dispatch") class ReauthenticateWebAuthnView(BaseReauthenticateView): form_class = ReauthenticateWebAuthnForm template_name = f"mfa/webauthn/reauthenticate.{account_settings.TEMPLATE_EXTENSION}" def get_form_class(self): return get_form_class( app_settings.FORMS, "reauthenticate_webauthn", self.form_class ) def get_form_kwargs(self) -> dict: ret = super().get_form_kwargs() ret["user"] = self.request.user return ret def form_invalid(self, form) -> HttpResponse: for message in form.errors.get("credential", []): get_account_adapter().add_message( self.request, messages.ERROR, message=message ) return HttpResponseRedirect(reverse("account_login")) def form_valid(self, form) -> HttpResponse: authenticator = form.cleaned_data["credential"] flows.reauthenticate(self.request, authenticator) return super().form_valid(form) def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data() request_options = auth.begin_authentication(self.request.user) ret["js_data"] = {"request_options": request_options} return ret reauthenticate_webauthn = ReauthenticateWebAuthnView.as_view() @method_decorator(reauthentication_required, name="dispatch") class EditWebAuthnView(NextRedirectMixin, UpdateView): form_class = EditWebAuthnForm template_name = f"mfa/webauthn/edit_form.{account_settings.TEMPLATE_EXTENSION}" success_url = reverse_lazy("mfa_list_webauthn") def get_form_class(self): return get_form_class(app_settings.FORMS, "edit_webauthn", self.form_class) def get_queryset(self): return Authenticator.objects.filter( user=self.request.user, type=Authenticator.Type.WEBAUTHN ) edit_webauthn = EditWebAuthnView.as_view() @method_decorator( login_stage_required( stage=PasskeySignupStage.key, redirect_urlname="account_signup" ), name="dispatch", ) class SignupWebAuthnView(FormView): form_class = SignupWebAuthnForm template_name = f"mfa/webauthn/signup_form.{account_settings.TEMPLATE_EXTENSION}" @property def _login_stage(self): return self.request._login_stage def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data() stage = self._login_stage creation_options = auth.begin_registration(stage.login.user, True) ret["js_data"] = {"creation_options": creation_options} return ret def get_form_class(self): return get_form_class(app_settings.FORMS, "signup_webauthn", self.form_class) def get_form_kwargs(self) -> dict: ret = super().get_form_kwargs() stage = self._login_stage ret["user"] = stage.login.user return ret def form_valid(self, form) -> HttpResponse: stage = self._login_stage flows.signup_authenticator( self.request, user=stage.login.user, name=form.cleaned_data["name"], credential=form.cleaned_data["credential"], ) return stage.exit() signup_webauthn = SignupWebAuthnView.as_view() ================================================ FILE: allauth/models.py ================================================ ================================================ FILE: allauth/ratelimit.py ================================================ import warnings from allauth.core.ratelimit import clear, consume, consume_or_429 __all__ = ["consume", "consume_or_429", "clear"] warnings.warn("allauth.ratelimit is deprecated, use allauth.core.ratelimit") ================================================ FILE: allauth/socialaccount/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/adapter.py ================================================ import functools import warnings from django.core.exceptions import ImproperlyConfigured, MultipleObjectsReturned from django.db.models import Q from django.urls import reverse from django.utils.crypto import get_random_string from django.utils.translation import gettext_lazy as _ from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.internal.emailkit import valid_email_or_none from allauth.account.utils import user_email, user_field, user_username from allauth.core.internal.adapter import BaseAdapter from allauth.core.internal.modelkit import deserialize_instance, serialize_instance from allauth.utils import import_attribute from . import app_settings class DefaultSocialAccountAdapter(BaseAdapter): """The adapter class allows you to override various functionality of the ``allauth.socialaccount`` app. To do so, point ``settings.SOCIALACCOUNT_ADAPTER`` to your own class that derives from ``DefaultSocialAccountAdapter`` and override the behavior by altering the implementation of the methods according to your own needs. """ error_messages = { "email_taken": _( "An account already exists with this email address." " Please sign in to that account first, then connect" " your %s account." ), "invalid_token": _("Invalid token."), "no_password": _("Your account has no password set up."), "no_verified_email": _("Your account has no verified email address."), "disconnect_last": _( "You cannot disconnect your last remaining third-party account." ), "connected_other": _( "The third-party account is already connected to a different account." ), } def pre_social_login(self, request, sociallogin): """ Invoked just after a user successfully authenticates via a social provider, but before the login is actually processed (and before the pre_social_login signal is emitted). You can use this hook to intervene, e.g. abort the login by raising an ImmediateHttpResponse Why both an adapter hook and the signal? Intervening in e.g. the flow from within a signal handler is bad -- multiple handlers may be active and are executed in undetermined order. """ pass def on_authentication_error( self, request, provider, error=None, exception=None, extra_context=None, ): """ Invoked when there is an error in the authentication cycle. In this case, pre_social_login will not be reached. You can use this hook to intervene, e.g. redirect to an educational flow by raising an ImmediateHttpResponse. """ if hasattr(self, "authentication_error"): warnings.warn( "adapter.authentication_error() is deprecated, use adapter.on_authentication_error()" ) self.authentication_error( request, provider.id, error=error, exception=exception, extra_context=extra_context, ) def new_user(self, request, sociallogin): """ Instantiates a new User instance. """ return get_account_adapter().new_user(request) def save_user(self, request, sociallogin, form=None): """ Saves a newly signed up social login. In case of auto-signup, the signup form is not available. """ u = sociallogin.user u.set_unusable_password() account_adapter = get_account_adapter() if form: account_adapter.save_user(request, u, form) else: account_adapter.populate_username(request, u) sociallogin.save(request) return u def populate_user(self, request, sociallogin, data): """ Hook that can be used to further populate the user instance. For convenience, we populate several common fields. Note that the user instance being populated represents a suggested User instance that represents the social user that is in the process of being logged in. The User instance need not be completely valid and conflict free. For example, verifying whether or not the username already exists, is not a responsibility. """ username = data.get("username") first_name = data.get("first_name") last_name = data.get("last_name") email = data.get("email") name = data.get("name") user = sociallogin.user user_username(user, username or "") user_email(user, valid_email_or_none(email) or "") name_parts = (name or "").partition(" ") user_field(user, "first_name", first_name or name_parts[0]) user_field(user, "last_name", last_name or name_parts[2]) return user def get_connect_redirect_url(self, request, socialaccount): """ Returns the default URL to redirect to after successfully connecting a social account. """ url = reverse("socialaccount_connections") return url def validate_disconnect(self, account, accounts) -> None: """ Validate whether or not the socialaccount account can be safely disconnected. """ pass def is_auto_signup_allowed(self, request, sociallogin): # If email is specified, check for duplicate and if so, no auto signup. auto_signup = app_settings.AUTO_SIGNUP return auto_signup def is_open_for_signup(self, request, sociallogin): """ Checks whether or not the site is open for signups. Next to simply returning True/False you can also intervene the regular flow by raising an ImmediateHttpResponse """ return get_account_adapter(request).is_open_for_signup(request) def get_signup_form_initial_data(self, sociallogin): user = sociallogin.user email = user_email(user) if not email and len(sociallogin.email_addresses) > 0: email = sociallogin.email_addresses[0].email initial = { "email": email or "", "username": user_username(user) or "", "first_name": user_field(user, "first_name") or "", "last_name": user_field(user, "last_name") or "", } return initial def deserialize_instance(self, model, data): return deserialize_instance(model, data) def serialize_instance(self, instance): return serialize_instance(instance) def list_providers(self, request): from allauth.socialaccount.providers import registry ret = [] provider_classes = registry.get_class_list() apps = self.list_apps(request) apps_map = {} for app in apps: apps_map.setdefault(app.provider, []).append(app) for provider_class in provider_classes: provider_apps = apps_map.get(provider_class.id, []) if not provider_apps: if provider_class.uses_apps: continue provider_apps = [None] for app in provider_apps: provider = provider_class(request=request, app=app) ret.append(provider) return ret def get_provider(self, request, provider, client_id=None): """Looks up a `provider`, supporting subproviders by looking up by `provider_id`. """ from allauth.socialaccount.providers import registry provider_class = registry.get_class(provider) if provider_class is None or provider_class.uses_apps: app = self.get_app(request, provider=provider, client_id=client_id) if not provider_class: # In this case, the `provider` argument passed was a # `provider_id`. provider_class = registry.get_class(app.provider) if not provider_class: raise ImproperlyConfigured(f"unknown provider: {app.provider}") return provider_class(request, app=app) elif provider_class: assert not provider_class.uses_apps # nosec return provider_class(request, app=None) else: raise ImproperlyConfigured(f"unknown provider: {provider}") def list_apps(self, request, provider=None, client_id=None): """SocialApp's can be setup in the database, or, via `settings.SOCIALACCOUNT_PROVIDERS`. This methods returns a uniform list of all known apps matching the specified criteria, and blends both (db/settings) sources of data. """ # NOTE: Avoid loading models at top due to registry boot... from allauth.socialaccount.models import SocialApp # Map provider to the list of apps. provider_to_apps = {} # First, populate it with the DB backed apps. if request: db_apps = SocialApp.objects.on_site(request) else: db_apps = SocialApp.objects.all() if provider: db_apps = db_apps.filter(Q(provider=provider) | Q(provider_id=provider)) if client_id: db_apps = db_apps.filter(client_id=client_id) for app in db_apps: apps = provider_to_apps.setdefault(app.provider, []) apps.append(app) # Then, extend it with the settings backed apps. for p, pcfg in app_settings.PROVIDERS.items(): app_configs = pcfg.get("APPS") if app_configs is None: app_config = pcfg.get("APP") if app_config is None: continue app_configs = [app_config] apps = provider_to_apps.setdefault(p, []) for config in app_configs: app = SocialApp(provider=p) for field in [ "name", "provider_id", "client_id", "secret", "key", "settings", ]: if field in config: setattr(app, field, config[field]) if "certificate_key" in config: warnings.warn("'certificate_key' should be moved into app.settings") app.settings["certificate_key"] = config["certificate_key"] if client_id and app.client_id != client_id: continue if ( provider and app.provider_id != provider and app.provider != provider ): continue apps.append(app) # Flatten the list of apps. apps = [] for provider_apps in provider_to_apps.values(): apps.extend(provider_apps) return apps def get_app(self, request, provider, client_id=None): from allauth.socialaccount.models import SocialApp apps = self.list_apps(request, provider=provider, client_id=client_id) if len(apps) > 1: visible_apps = [app for app in apps if not app.settings.get("hidden")] if len(visible_apps) != 1: raise MultipleObjectsReturned apps = visible_apps elif len(apps) == 0: raise SocialApp.DoesNotExist() return apps[0] def send_notification_mail(self, *args, **kwargs): return get_account_adapter().send_notification_mail(*args, **kwargs) def get_requests_session(self): import requests session = requests.Session() session.request = functools.partial( session.request, timeout=app_settings.REQUESTS_TIMEOUT ) return session def is_email_verified(self, provider, email): """ Returns ``True`` iff the given email encountered during a social login for the given provider is to be assumed verified. This can be configured with a ``"verified_email"`` key in the provider app settings, or a ``"VERIFIED_EMAIL"`` in the global provider settings (``SOCIALACCOUNT_PROVIDERS``). Both can be set to ``False`` or ``True``, or, a list of domains to match email addresses against. """ verified_email = None if provider.app: verified_email = provider.app.settings.get("verified_email") if verified_email is None: settings = provider.get_settings() verified_email = settings.get("VERIFIED_EMAIL", False) if isinstance(verified_email, bool): pass elif isinstance(verified_email, list): email_domain = email.partition("@")[2].lower() verified_domains = [d.lower() for d in verified_email] verified_email = email_domain in verified_domains else: raise ImproperlyConfigured("verified_email wrongly configured") return verified_email def can_authenticate_by_email(self, login, email): """ Returns ``True`` iff authentication by email is active for this login/email. This can be configured with a ``"email_authentication"`` key in the provider app settings, or a ``"VERIFIED_EMAIL"`` in the global provider settings (``SOCIALACCOUNT_PROVIDERS``). """ ret = None provider = login.provider if provider.app: ret = provider.app.settings.get("email_authentication") if ret is None: ret = app_settings.EMAIL_AUTHENTICATION or provider.get_settings().get( "EMAIL_AUTHENTICATION", False ) return ret def generate_state_param(self, state: dict) -> str: """ To preserve certain state before the handshake with the provider takes place, and be able to verify/use that state later on, a `state` parameter is typically passed to the provider. By default, a random string sufficies as the state parameter value is actually just a reference/pointer to the actual state. You can use this adapter method to alter the generation of the `state` parameter. """ from allauth.socialaccount.internal.statekit import STATE_ID_LENGTH return get_random_string(STATE_ID_LENGTH) def get_adapter(request=None): return import_attribute(app_settings.ADAPTER)(request) ================================================ FILE: allauth/socialaccount/admin.py ================================================ from django import forms from django.contrib import admin from allauth import app_settings from allauth.account.adapter import get_adapter from allauth.socialaccount import providers from allauth.socialaccount.models import SocialAccount, SocialApp, SocialToken class SocialAppForm(forms.ModelForm): class Meta: model = SocialApp exclude: list[str] = [] widgets = { "client_id": forms.TextInput(attrs={"size": "100"}), "key": forms.TextInput(attrs={"size": "100"}), "secret": forms.TextInput(attrs={"size": "100"}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["provider"] = forms.ChoiceField( choices=providers.registry.as_choices() ) class SocialAppAdmin(admin.ModelAdmin): form = SocialAppForm list_display = ( "name", "provider", ) filter_horizontal = ("sites",) if app_settings.SITES_ENABLED else () class SocialAccountAdmin(admin.ModelAdmin): search_fields = ["uid"] raw_id_fields = ("user",) list_display = ("user", "uid", "provider") list_filter = ("provider",) def get_search_fields(self, request): search_fields = super().get_search_fields(request) user_search_fields = get_adapter().get_user_search_fields() return search_fields + list(map(lambda a: f"user__{a}", user_search_fields)) class SocialTokenAdmin(admin.ModelAdmin): raw_id_fields = ( "app", "account", ) list_display = ("app", "account", "truncated_token", "expires_at") list_filter = ("app", "app__provider", "expires_at") def truncated_token(self, token): max_chars = 40 ret = token.token if len(ret) > max_chars: ret = f"{ret[0:max_chars]}...(truncated)" return ret truncated_token.short_description = "Token" # type: ignore[attr-defined] admin.site.register(SocialApp, SocialAppAdmin) admin.site.register(SocialToken, SocialTokenAdmin) admin.site.register(SocialAccount, SocialAccountAdmin) ================================================ FILE: allauth/socialaccount/app_settings.py ================================================ class AppSettings: def __init__(self, prefix: str) -> None: self.prefix = prefix def _setting(self, name: str, dflt): from allauth.utils import get_setting return get_setting(self.prefix + name, dflt) @property def QUERY_EMAIL(self) -> bool: """ Request email address from 3rd party account provider? E.g. using OpenID AX """ return self._setting("QUERY_EMAIL", self.EMAIL_REQUIRED) @property def AUTO_SIGNUP(self) -> bool: """ Attempt to bypass the signup form by using fields (e.g. username, email) retrieved from the social account provider. If a conflict arises due to a duplicate email signup form will still kick in. """ return self._setting("AUTO_SIGNUP", True) @property def PROVIDERS(self) -> dict: """ Provider specific settings """ ret = self._setting("PROVIDERS", {}) oidc = ret.get("openid_connect") if oidc: ret["openid_connect"] = self._migrate_oidc(oidc) return ret def _migrate_oidc(self, oidc: dict) -> dict: servers = oidc.get("SERVERS") if servers is None: return oidc ret = {} apps = [] for server in servers: app = dict(**server["APP"]) app_settings = {} if "token_auth_method" in server: app_settings["token_auth_method"] = server["token_auth_method"] app_settings["server_url"] = server["server_url"] app.update( { "name": server.get("name", ""), "provider_id": server["id"], "settings": app_settings, } ) assert app["provider_id"] # nosec apps.append(app) ret["APPS"] = apps return ret @property def EMAIL_REQUIRED(self) -> bool: """ The user is required to hand over an email address when signing up """ from allauth.account import app_settings as account_settings fields = account_settings.SIGNUP_FIELDS email_required = "email" in fields and fields["email"].get("required") return self._setting("EMAIL_REQUIRED", email_required) @property def EMAIL_VERIFICATION(self): """ See email verification method. When `None`, the default `allauth.account` logic kicks in. """ from allauth import app_settings as allauth_settings from allauth.account import app_settings as account_settings dflt = ( account_settings.EmailVerificationMethod.NONE if allauth_settings.SOCIALACCOUNT_ONLY else None ) return self._setting("EMAIL_VERIFICATION", dflt) @property def EMAIL_AUTHENTICATION(self) -> bool: """Consider a scenario where a social login occurs, and the social account comes with a verified email address (verified by the account provider), but that email address is already taken by a local user account. Additionally, assume that the local user account does not have any social account connected. Now, if the provider can be fully trusted, you can argue that we should treat this scenario as a login to the existing local user account even if the local account does not already have the social account connected, because -- according to the provider -- the user logging in has ownership of the email address. This is how this scenario is handled when `EMAIL_AUTHENTICATION` is set to `True`. As this implies that an untrustworthy provider can login to any local account by fabricating social account data, this setting defaults to `False`. Only set it to `True` if you are using providers that can be fully trusted. """ return self._setting("EMAIL_AUTHENTICATION", False) @property def EMAIL_AUTHENTICATION_AUTO_CONNECT(self) -> bool: """In case email authentication is applied, this setting controls whether or not the social account is automatically connected to the local account. In case of ``False`` (the default) the local account remains unchanged during the login. In case of ``True``, the social account for which the email matched, is automatically added to the list of social accounts connected to the local account. As a result, even if the user were to change the email address afterwards, social login would still be possible when using ``True``, but not in case of ``False``. """ return self._setting("EMAIL_AUTHENTICATION_AUTO_CONNECT", False) @property def ADAPTER(self) -> str: return self._setting( "ADAPTER", "allauth.socialaccount.adapter.DefaultSocialAccountAdapter", ) @property def FORMS(self) -> dict: return self._setting("FORMS", {}) @property def LOGIN_ON_GET(self) -> bool: return self._setting("LOGIN_ON_GET", False) @property def STORE_TOKENS(self) -> bool: return self._setting("STORE_TOKENS", False) @property def UID_MAX_LENGTH(self) -> int: return 191 @property def SOCIALACCOUNT_STR(self): return self._setting("SOCIALACCOUNT_STR", None) @property def REQUESTS_TIMEOUT(self) -> int: return self._setting("REQUESTS_TIMEOUT", 5) @property def OPENID_CONNECT_URL_PREFIX(self) -> str: return self._setting("OPENID_CONNECT_URL_PREFIX", "oidc") _app_settings = AppSettings("SOCIALACCOUNT_") def __getattr__(name): # See https://peps.python.org/pep-0562/ return getattr(_app_settings, name) ================================================ FILE: allauth/socialaccount/apps.py ================================================ from django.apps import AppConfig from django.utils.translation import gettext_lazy as _ from allauth import app_settings class SocialAccountConfig(AppConfig): name = "allauth.socialaccount" verbose_name = _("Social Accounts") default_auto_field = app_settings.DEFAULT_AUTO_FIELD or "django.db.models.AutoField" def ready(self): from allauth.socialaccount import checks # noqa from allauth.socialaccount.providers import registry registry.load() ================================================ FILE: allauth/socialaccount/checks.py ================================================ from django.core.checks import Critical, register @register() def settings_check(app_configs, **kwargs): from allauth import app_settings as allauth_settings from allauth.account import app_settings as account_settings from allauth.socialaccount import app_settings ret = [] if allauth_settings.SOCIALACCOUNT_ONLY: if ( app_settings.EMAIL_VERIFICATION != account_settings.EmailVerificationMethod.NONE ): ret.append( Critical( msg="SOCIALACCOUNT_ONLY requires SOCIALACCOUNT_EMAIL_VERIFICATION = 'none'" ) ) return ret ================================================ FILE: allauth/socialaccount/forms.py ================================================ from django import forms from allauth.account.forms import BaseSignupForm from allauth.socialaccount.internal import flows from . import app_settings from .adapter import get_adapter from .models import SocialAccount class SignupForm(BaseSignupForm): def __init__(self, *args, **kwargs) -> None: self.sociallogin = kwargs.pop("sociallogin") initial = get_adapter().get_signup_form_initial_data(self.sociallogin) kwargs.update( { "initial": initial, "email_required": kwargs.get( "email_required", app_settings.EMAIL_REQUIRED ), } ) super().__init__(*args, **kwargs) def save(self, request): adapter = get_adapter() user = adapter.save_user(request, self.sociallogin, form=self) self.custom_signup(request, user) return user def validate_unique_email(self, value) -> str: try: return super().validate_unique_email(value) except forms.ValidationError: raise get_adapter().validation_error( "email_taken", self.sociallogin.provider.name ) class DisconnectForm(forms.Form): account = forms.ModelChoiceField( queryset=SocialAccount.objects.none(), widget=forms.RadioSelect, required=True, ) def __init__(self, *args, **kwargs): self.request = kwargs.pop("request") self.accounts = SocialAccount.objects.filter(user=self.request.user) super().__init__(*args, **kwargs) self.fields["account"].queryset = self.accounts def clean(self): cleaned_data = super().clean() account = cleaned_data.get("account") if account: flows.connect.validate_disconnect(self.request, account) return cleaned_data def save(self) -> None: account = self.cleaned_data["account"] flows.connect.disconnect(self.request, account) ================================================ FILE: allauth/socialaccount/helpers.py ================================================ from http import HTTPStatus from django.http import HttpResponseRedirect from django.shortcuts import render from django.urls import reverse from allauth import app_settings as allauth_settings from allauth.account import app_settings as account_settings from allauth.account.utils import user_display from allauth.core.exceptions import ImmediateHttpResponse from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.internal import flows from allauth.socialaccount.providers.base import AuthError def render_authentication_error( request, provider, error=AuthError.UNKNOWN, exception=None, extra_context=None, ): try: if extra_context is None: extra_context = {} get_adapter().on_authentication_error( request, provider, error=error, exception=exception, extra_context=extra_context, ) if allauth_settings.HEADLESS_ENABLED: from allauth.headless.socialaccount import internal internal.on_authentication_error( request, provider=provider, error=error, exception=exception, extra_context=extra_context, ) except ImmediateHttpResponse as e: return e.response if error == AuthError.CANCELLED: return HttpResponseRedirect(reverse("socialaccount_login_cancelled")) context = { "auth_error": { "provider": provider, "code": error, "exception": exception, } } context.update(extra_context) return render( request, f"socialaccount/authentication_error.{account_settings.TEMPLATE_EXTENSION}", context, status=HTTPStatus.UNAUTHORIZED, ) def complete_social_login(request, sociallogin): if sociallogin.is_headless: from allauth.headless.socialaccount import internal return internal.complete_login(request, sociallogin) return flows.login.complete_login(request, sociallogin) def socialaccount_user_display(socialaccount): func = app_settings.SOCIALACCOUNT_STR if not func: return user_display(socialaccount.user) return func(socialaccount) ================================================ FILE: allauth/socialaccount/internal/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/internal/flows/__init__.py ================================================ from allauth.socialaccount.internal.flows import ( connect, email_authentication, login, signup, ) __all__ = ["connect", "login", "signup", "email_authentication"] ================================================ FILE: allauth/socialaccount/internal/flows/connect.py ================================================ from django.contrib import messages from django.core.exceptions import PermissionDenied, ValidationError from django.http import HttpResponseRedirect from allauth import app_settings as allauth_settings from allauth.account import app_settings as account_settings from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.internal import flows from allauth.account.models import EmailAddress from allauth.core.exceptions import ReauthenticationRequired from allauth.socialaccount import signals from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.models import SocialAccount, SocialLogin def validate_disconnect(request, account) -> None: """ Validate whether or not the socialaccount account can be safely disconnected. """ accounts = SocialAccount.objects.filter(user_id=account.user_id) is_last = not accounts.exclude(pk=account.pk).exists() adapter = get_adapter() if is_last: if allauth_settings.SOCIALACCOUNT_ONLY: raise adapter.validation_error("disconnect_last") # No usable password would render the local account unusable if not account.user.has_usable_password(): raise adapter.validation_error("no_password") # No email address, no password reset if ( account_settings.EMAIL_VERIFICATION == account_settings.EmailVerificationMethod.MANDATORY ): if not EmailAddress.objects.filter( user=account.user, verified=True ).exists(): raise adapter.validation_error("no_verified_email") adapter.validate_disconnect(account, accounts) def disconnect(request, account) -> None: if account_settings.REAUTHENTICATION_REQUIRED: flows.reauthentication.raise_if_reauthentication_required(request) get_account_adapter().add_message( request, messages.INFO, "socialaccount/messages/account_disconnected.txt", ) provider = account.get_provider() account.delete() signals.social_account_removed.send( sender=SocialAccount, request=request, socialaccount=account ) get_adapter().send_notification_mail( "socialaccount/email/account_disconnected", request.user, context={ "account": account, "provider": provider, }, ) def resume_connect(request, serialized_state): sociallogin = SocialLogin.deserialize(serialized_state) return connect(request, sociallogin) def connect(request, sociallogin): try: ok, action, message = do_connect(request, sociallogin) except PermissionDenied: # This should not happen. Simply redirect to the connections # view (which has a login required) connect_redirect_url = get_adapter().get_connect_redirect_url( request, sociallogin.account ) return HttpResponseRedirect(connect_redirect_url) except ReauthenticationRequired: return flows.reauthentication.stash_and_reauthenticate( request, sociallogin.serialize(), "allauth.socialaccount.internal.flows.connect.resume_connect", ) except ValidationError: ok, action, message = ( False, None, "socialaccount/messages/account_connected_other.txt", ) level = messages.INFO if ok else messages.ERROR default_next = get_adapter().get_connect_redirect_url(request, sociallogin.account) next_url = sociallogin.get_redirect_url(request) or default_next get_account_adapter(request).add_message( request, level, message, message_context={"sociallogin": sociallogin, "action": action}, ) return HttpResponseRedirect(next_url) def do_connect(request, sociallogin): if request.user.is_anonymous: raise PermissionDenied() if account_settings.REAUTHENTICATION_REQUIRED: flows.reauthentication.raise_if_reauthentication_required(request) message = "socialaccount/messages/account_connected.txt" action = None ok = True if sociallogin.is_existing: if sociallogin.user != request.user: # Social account of other user. For now, this scenario # is not supported. Issue is that one cannot simply # remove the social account from the other user, as # that may render the account unusable. raise get_adapter().validation_error("connected_other") elif not sociallogin.account._state.adding: action = "updated" message = "socialaccount/messages/account_connected_updated.txt" else: action = "added" sociallogin.connect(request, request.user) else: # New account, let's connect action = "added" sociallogin.connect(request, request.user) return ok, action, message ================================================ FILE: allauth/socialaccount/internal/flows/email_authentication.py ================================================ from allauth import app_settings as allauth_settings from allauth.account.models import EmailAddress def wipe_password(request, user, email: str) -> None: """ Consider a scenario where an attacker signs up for an account using the email address of a victim. Obviously, the email address cannot be verified, yet the attacker -- knowing the password -- can wait until the victim appears. When the victim signs in using email authentication, it is not obvious that the victim is signing into an account that was not created by the victim. As a result, both the attacker and the victim now have access to the account. To prevent this, we wipe the password of the account in case the email address was not verified, effectively locking out the attacker. """ try: address = EmailAddress.objects.get_for_user(user, email) except EmailAddress.DoesNotExist: address = None if address and address.verified: # Verified email address, no reason to worry. return if user.has_usable_password(): user.set_unusable_password() user.save(update_fields=["password"]) # Also wipe any other sessions (upstream integrators may hook up to the # ending of the sessions to trigger e.g. backchannel logout. if allauth_settings.USERSESSIONS_ENABLED: from allauth.usersessions.internal.flows.sessions import end_other_sessions end_other_sessions(request, user) ================================================ FILE: allauth/socialaccount/internal/flows/login.py ================================================ from django.http import HttpResponseRedirect from django.shortcuts import render from allauth.account import app_settings as account_settings from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.utils import perform_login from allauth.core.exceptions import ImmediateHttpResponse, SignupClosedException from allauth.socialaccount import app_settings, signals from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.internal.flows.connect import connect, do_connect from allauth.socialaccount.internal.flows.signup import ( clear_pending_signup, process_signup, ) from allauth.socialaccount.models import SocialLogin from allauth.socialaccount.providers.base import AuthProcess def _login(request, sociallogin): sociallogin._accept_login(request) record_authentication(request, sociallogin) return perform_login( request, sociallogin.user, email_verification=app_settings.EMAIL_VERIFICATION, redirect_url=sociallogin.get_redirect_url(request), signal_kwargs={"sociallogin": sociallogin}, ) def pre_social_login(request, sociallogin) -> None: clear_pending_signup(request) assert not sociallogin.is_existing # nosec sociallogin.lookup() get_adapter().pre_social_login(request, sociallogin) signals.pre_social_login.send( sender=SocialLogin, request=request, sociallogin=sociallogin ) def complete_login(request, sociallogin, raises=False): try: pre_social_login(request, sociallogin) process = sociallogin.state.get("process") if process == AuthProcess.REDIRECT: return _redirect(request, sociallogin) elif process == AuthProcess.CONNECT: if raises: do_connect(request, sociallogin) else: return connect(request, sociallogin) else: return _authenticate(request, sociallogin) except SignupClosedException: if raises: raise return render( request, f"account/signup_closed.{account_settings.TEMPLATE_EXTENSION}", ) except ImmediateHttpResponse as e: if raises: raise return e.response def _redirect(request, sociallogin): next_url = sociallogin.get_redirect_url(request) or "/" return HttpResponseRedirect(next_url) def _authenticate(request, sociallogin): if request.user.is_authenticated: get_account_adapter(request).logout(request) if sociallogin.is_existing: # Login existing user ret = _login(request, sociallogin) else: # New social user ret = process_signup(request, sociallogin) return ret def record_authentication(request, sociallogin) -> None: from allauth.account.internal.flows.login import record_authentication record_authentication( request, sociallogin.user, "socialaccount", **{ "provider": sociallogin.account.provider, "uid": sociallogin.account.uid, }, ) ================================================ FILE: allauth/socialaccount/internal/flows/signup.py ================================================ from django.forms import ValidationError from allauth.account import app_settings as account_settings from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.internal.flows.manage_email import assess_unique_email from allauth.account.internal.flows.signup import complete_signup, prevent_enumeration from allauth.account.utils import user_username from allauth.core.exceptions import SignupClosedException from allauth.core.internal.httpkit import headed_redirect_response from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.models import SocialLogin def get_pending_signup(request) -> SocialLogin | None: if data := request.session.get("socialaccount_sociallogin"): try: return SocialLogin.deserialize(data) except ValueError: return None return None def redirect_to_signup(request, sociallogin): request.session["socialaccount_sociallogin"] = sociallogin.serialize() return headed_redirect_response("socialaccount_signup") def clear_pending_signup(request) -> None: request.session.pop("socialaccount_sociallogin", None) def signup_by_form(request, sociallogin, form): clear_pending_signup(request) user, resp = form.try_save(request) if not resp: resp = complete_social_signup(request, sociallogin) return resp def process_auto_signup(request, sociallogin): auto_signup = get_adapter().is_auto_signup_allowed(request, sociallogin) if not auto_signup: return False, None auto_signup, resp = process_auto_signup_email(request, sociallogin) if not auto_signup: return False, resp auto_signup, resp = process_auto_signup_phone(request, sociallogin) return auto_signup, resp def process_auto_signup_email(request, sociallogin): email = None if sociallogin.email_addresses: email = sociallogin.email_addresses[0].email # Let's check if auto_signup is really possible... if email: assessment = assess_unique_email(email) if assessment is True: # Auto signup is fine. auto_signup = True elif assessment is False: # Oops, another user already has this address. We cannot simply # connect this social account to the existing user. Reason is # that the email address may not be verified, meaning, the user # may be a hacker that has added your email address to their # account in the hope that you fall in their trap. We cannot # check on 'email_address.verified' either, because # 'email_address' is not guaranteed to be verified. auto_signup = False # TODO: We redirect to signup form -- user will see email # address conflict only after posting whereas we detected it # here already. else: assert assessment is None # nosec # Prevent enumeration is properly turned on, meaning, we cannot # show the signup form to allow the user to input another email # address. Instead, we're going to send the user an email that # the account already exists, and on the outside make it appear # as if an email verification mail was sent. resp = prevent_enumeration(request, email=email) return False, resp elif app_settings.EMAIL_REQUIRED: # Nope, email is required and we don't have it yet... auto_signup = False else: auto_signup = True return auto_signup, None def process_auto_signup_phone(request, sociallogin): # At this point, email is not required, or, we have a unique email. Let's # check the phone number. phone_field = account_settings.SIGNUP_FIELDS.get("phone") if not phone_field or not phone_field["required"]: return True, None if not sociallogin.phone: return False, None # If the phone is already taken? existing_user = get_account_adapter().get_user_by_phone(sociallogin.phone) if existing_user: return False, None return True, None def process_signup(request, sociallogin): if not get_adapter().is_open_for_signup(request, sociallogin): raise SignupClosedException() auto_signup, resp = process_auto_signup(request, sociallogin) if resp: return resp if not auto_signup: resp = redirect_to_signup(request, sociallogin) else: # Ok, auto signup it is, at least the email address is ok. # We still need to check the username though... if account_settings.USER_MODEL_USERNAME_FIELD: username = user_username(sociallogin.user) try: get_account_adapter(request).clean_username(username) except ValidationError: # This username is no good ... user_username(sociallogin.user, "") # TODO: This part contains a lot of duplication of logic # ("closed" rendering, create user, send email, in active # etc..) get_adapter().save_user(request, sociallogin, form=None) resp = complete_social_signup(request, sociallogin) return resp def complete_social_signup(request, sociallogin): from allauth.socialaccount.internal.flows.login import record_authentication record_authentication(request, sociallogin) return complete_signup( request, user=sociallogin.user, email_verification=app_settings.EMAIL_VERIFICATION, redirect_url=sociallogin.get_redirect_url(request), signal_kwargs={"sociallogin": sociallogin}, ) ================================================ FILE: allauth/socialaccount/internal/jwtkit.py ================================================ import json import time from django.core.cache import cache import jwt from cryptography.hazmat.backends import default_backend from cryptography.x509 import load_pem_x509_certificate from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Error def lookup_kid_pem_x509_certificate(keys_data, kid): """ Looks up the key given keys data of the form: {"": "-----BEGIN CERTIFICATE-----\nCERTIFICATE"} """ key = keys_data.get(kid) if key: public_key = load_pem_x509_certificate( key.encode("utf8"), default_backend() ).public_key() return public_key def lookup_kid_jwk(keys_data, kid): """ Looks up the key given keys data of the form: { "keys": [ { "kty": "RSA", "kid": "W6WcOKB", "use": "sig", "alg": "RS256", "n": "2Zc5d0-zk....", "e": "AQAB" }] } """ for d in keys_data["keys"]: if d["kid"] == kid: public_key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(d)) return public_key def fetch_key(credential, keys_url, lookup): header = jwt.get_unverified_header(credential) # {'alg': 'RS256', 'kid': '0ad1fec78504f447bae65bcf5afaedb65eec9e81', 'typ': 'JWT'} kid = header["kid"] alg = header["alg"] with get_adapter().get_requests_session() as sess: response = sess.get(keys_url) response.raise_for_status() keys_data = response.json() key = lookup(keys_data, kid) if not key: raise OAuth2Error(f"Invalid 'kid': '{kid}'") return alg, key def verify_jti(data: dict) -> None: """ Put the JWT token on a blacklist to prevent replay attacks. """ iss = data.get("iss") exp = data.get("exp") jti = data.get("jti") if iss is None or exp is None or jti is None: return timeout = exp - time.time() key = f"jwt:iss={iss},jti={jti}" if not cache.add(key=key, value=True, timeout=timeout): raise OAuth2Error("token already used") def verify_and_decode( *, credential, keys_url, issuer, audience, lookup_kid, verify_signature: bool = True ) -> dict: try: if verify_signature: alg, key = fetch_key(credential, keys_url, lookup_kid) algorithms = [alg] else: key = "" algorithms = None data = jwt.decode( credential, key=key, options={ "verify_signature": verify_signature, "verify_iss": True, "verify_aud": True, "verify_exp": True, }, issuer=issuer, audience=audience, algorithms=algorithms, ) verify_jti(data) return data except jwt.PyJWTError as e: raise OAuth2Error("Invalid id_token") from e ================================================ FILE: allauth/socialaccount/internal/statekit.py ================================================ import time from typing import Any from allauth.socialaccount.adapter import get_adapter STATE_ID_LENGTH = 16 MAX_STATES = 10 STATES_SESSION_KEY = "socialaccount_states" def get_oldest_state( states: dict[str, tuple[dict[str, Any], float]], rev: bool = False ) -> tuple[str | None, dict[str, Any] | None]: oldest_ts = None oldest_id = None oldest = None for state_id, state_ts in states.items(): ts = state_ts[1] if oldest_ts is None or ( (rev and ts > oldest_ts) or ((not rev) and oldest_ts > ts) ): oldest_ts = ts oldest_id = state_id oldest = state_ts[0] return oldest_id, oldest def gc_states(states: dict[str, tuple[dict[str, Any], float]]) -> None: if len(states) > MAX_STATES: oldest_id, oldest = get_oldest_state(states) if oldest_id: del states[oldest_id] def get_states(request) -> dict[str, tuple[dict[str, Any], float]]: states = request.session.get(STATES_SESSION_KEY) if not isinstance(states, dict): states = {} return states def stash_state(request, state: dict[str, Any], state_id: str | None = None) -> str: states = get_states(request) gc_states(states) if state_id is None: state_id = get_adapter().generate_state_param(state) states[state_id] = (state, time.time()) request.session[STATES_SESSION_KEY] = states return state_id def unstash_state(request, state_id: str) -> dict[str, Any] | None: state: dict[str, Any] | None = None states = get_states(request) state_ts = states.get(state_id) if state_ts is not None: state = state_ts[0] del states[state_id] request.session[STATES_SESSION_KEY] = states return state def unstash_last_state(request) -> dict[str, Any] | None: states = get_states(request) state_id, state = get_oldest_state(states, rev=True) if state_id: unstash_state(request, state_id) return state ================================================ FILE: allauth/socialaccount/migrations/0001_initial.py ================================================ from django.conf import settings from django.db import migrations, models from allauth import app_settings class Migration(migrations.Migration): dependencies = ( [ ("sites", "0001_initial"), ] if app_settings.SITES_ENABLED else [] ) + [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name="SocialAccount", fields=[ ( "id", models.AutoField( verbose_name="ID", serialize=False, auto_created=True, primary_key=True, ), ), ( "provider", models.CharField( max_length=30, verbose_name="provider", ), ), ( "uid", models.CharField( max_length=getattr( settings, "SOCIALACCOUNT_UID_MAX_LENGTH", 191 ), verbose_name="uid", ), ), ( "last_login", models.DateTimeField(auto_now=True, verbose_name="last login"), ), ( "date_joined", models.DateTimeField(auto_now_add=True, verbose_name="date joined"), ), ( "extra_data", models.TextField(default="{}", verbose_name="extra data"), ), ( "user", models.ForeignKey( to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE ), ), ], options={ "verbose_name": "social account", "verbose_name_plural": "social accounts", }, bases=(models.Model,), ), migrations.CreateModel( name="SocialApp", fields=[ ( "id", models.AutoField( verbose_name="ID", serialize=False, auto_created=True, primary_key=True, ), ), ( "provider", models.CharField( max_length=30, verbose_name="provider", ), ), ("name", models.CharField(max_length=40, verbose_name="name")), ( "client_id", models.CharField( help_text="App ID, or consumer key", max_length=100, verbose_name="client id", ), ), ( "secret", models.CharField( help_text="API secret, client secret, or consumer secret", max_length=100, verbose_name="secret key", ), ), ( "key", models.CharField( help_text="Key", max_length=100, verbose_name="key", blank=True, ), ), ] + ( [ ("sites", models.ManyToManyField(to="sites.Site", blank=True)), ] if app_settings.SITES_ENABLED else [] ), options={ "verbose_name": "social application", "verbose_name_plural": "social applications", }, bases=(models.Model,), ), migrations.CreateModel( name="SocialToken", fields=[ ( "id", models.AutoField( verbose_name="ID", serialize=False, auto_created=True, primary_key=True, ), ), ( "token", models.TextField( help_text='"oauth_token" (OAuth1) or access token (OAuth2)', verbose_name="token", ), ), ( "token_secret", models.TextField( help_text='"oauth_token_secret" (OAuth1) or refresh token (OAuth2)', verbose_name="token secret", blank=True, ), ), ( "expires_at", models.DateTimeField( null=True, verbose_name="expires at", blank=True ), ), ( "account", models.ForeignKey( to="socialaccount.SocialAccount", on_delete=models.CASCADE, ), ), ( "app", models.ForeignKey( to="socialaccount.SocialApp", on_delete=models.CASCADE ), ), ], options={ "verbose_name": "social application token", "verbose_name_plural": "social application tokens", }, bases=(models.Model,), ), migrations.AlterUniqueTogether( name="socialtoken", unique_together={("app", "account")}, ), migrations.AlterUniqueTogether( name="socialaccount", unique_together={("provider", "uid")}, ), ] ================================================ FILE: allauth/socialaccount/migrations/0002_token_max_lengths.py ================================================ from django.conf import settings from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("socialaccount", "0001_initial"), ] operations = [ migrations.AlterField( model_name="socialaccount", name="uid", field=models.CharField( max_length=getattr(settings, "SOCIALACCOUNT_UID_MAX_LENGTH", 191), verbose_name="uid", ), ), migrations.AlterField( model_name="socialapp", name="client_id", field=models.CharField( help_text="App ID, or consumer key", max_length=191, verbose_name="client id", ), ), migrations.AlterField( model_name="socialapp", name="key", field=models.CharField( help_text="Key", max_length=191, verbose_name="key", blank=True ), ), migrations.AlterField( model_name="socialapp", name="secret", field=models.CharField( help_text="API secret, client secret, or consumer secret", max_length=191, verbose_name="secret key", blank=True, ), ), ] ================================================ FILE: allauth/socialaccount/migrations/0003_extra_data_default_dict.py ================================================ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("socialaccount", "0002_token_max_lengths"), ] operations = [ migrations.AlterField( model_name="socialaccount", name="extra_data", field=models.TextField(default="{}", verbose_name="extra data"), preserve_default=True, ), ] ================================================ FILE: allauth/socialaccount/migrations/0004_app_provider_id_settings.py ================================================ # Generated by Django 3.2.19 on 2023-06-30 13:16 from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("socialaccount", "0003_extra_data_default_dict"), ] operations = [ migrations.AddField( model_name="socialapp", name="provider_id", field=models.CharField( blank=True, max_length=200, verbose_name="provider ID" ), ), migrations.AddField( model_name="socialapp", name="settings", field=models.JSONField(blank=True, default=dict), ), migrations.AlterField( model_name="socialaccount", name="provider", field=models.CharField(max_length=200, verbose_name="provider"), ), ] ================================================ FILE: allauth/socialaccount/migrations/0005_socialtoken_nullable_app.py ================================================ # Generated by Django 3.2.20 on 2023-09-03 19:46 import django.db.models.deletion from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("socialaccount", "0004_app_provider_id_settings"), ] operations = [ migrations.AlterField( model_name="socialtoken", name="app", field=models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to="socialaccount.socialapp", ), ), ] ================================================ FILE: allauth/socialaccount/migrations/0006_alter_socialaccount_extra_data.py ================================================ # Generated by Django 3.2.20 on 2023-10-11 09:23 from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("socialaccount", "0005_socialtoken_nullable_app"), ] operations = [ migrations.AlterField( model_name="socialaccount", name="extra_data", field=models.JSONField(default=dict, verbose_name="extra data"), ), ] ================================================ FILE: allauth/socialaccount/migrations/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/models.py ================================================ from typing import Any from django.conf import settings from django.contrib.auth import authenticate, get_user_model from django.contrib.sites.shortcuts import get_current_site from django.core.exceptions import ImproperlyConfigured, PermissionDenied from django.db import models from django.utils.translation import gettext_lazy as _ import allauth.app_settings from allauth import app_settings as allauth_settings from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.models import EmailAddress from allauth.account.utils import ( filter_users_by_email, get_next_redirect_url, setup_user_email, ) from allauth.core import context from allauth.socialaccount import app_settings, providers, signals from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.internal import statekit from allauth.utils import get_request_param if not allauth_settings.SOCIALACCOUNT_ENABLED: raise ImproperlyConfigured( "allauth.socialaccount not installed, yet its models are imported." ) class SocialAppManager(models.Manager): def on_site(self, request): if allauth.app_settings.SITES_ENABLED: site = get_current_site(request) return self.filter(sites__id=site.id) return self.all() class SocialApp(models.Model): objects = SocialAppManager() # The provider type, e.g. "google", "telegram", "saml". provider = models.CharField( verbose_name=_("provider"), max_length=30, ) # For providers that support subproviders, such as OpenID Connect and SAML, # this ID identifies that instance. SocialAccount's originating from app # will have their `provider` field set to the `provider_id` if available, # else `provider`. provider_id = models.CharField( verbose_name=_("provider ID"), max_length=200, blank=True, ) name = models.CharField(verbose_name=_("name"), max_length=40) client_id = models.CharField( verbose_name=_("client id"), max_length=191, help_text=_("App ID, or consumer key"), ) secret = models.CharField( verbose_name=_("secret key"), max_length=191, blank=True, help_text=_("API secret, client secret, or consumer secret"), ) key = models.CharField( verbose_name=_("key"), max_length=191, blank=True, help_text=_("Key") ) settings = models.JSONField(default=dict, blank=True) if allauth.app_settings.SITES_ENABLED: # Most apps can be used across multiple domains, therefore we use # a ManyToManyField. Note that Facebook requires an app per domain # (unless the domains share a common base name). # blank=True allows for disabling apps without removing them sites = models.ManyToManyField("sites.Site", blank=True) # type: ignore[var-annotated] class Meta: verbose_name = _("social application") verbose_name_plural = _("social applications") def __str__(self) -> str: return self.name def get_provider(self, request): provider_class = providers.registry.get_class(self.provider) return provider_class(request=request, app=self) class SocialAccount(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) # Given a `SocialApp` from which this account originates, this field equals # the app's `app.provider_id` if available, `app.provider` otherwise. provider = models.CharField( verbose_name=_("provider"), max_length=200, ) # Just in case you're wondering if an OpenID identity URL is going # to fit in a 'uid': # # Ideally, URLField(max_length=1024, unique=True) would be used # for identity. However, MySQL has a max_length limitation of 191 # for URLField (in case of utf8mb4). How about # models.TextField(unique=True) then? Well, that won't work # either for MySQL due to another bug[1]. So the only way out # would be to drop the unique constraint, or switch to shorter # identity URLs. Opted for the latter, as [2] suggests that # identity URLs are supposed to be short anyway, at least for the # old spec. # # [1] https://code.djangoproject.com/ticket/2495. # [2] https://openid.net/specs/openid-authentication-1_1.html#limits uid = models.CharField( verbose_name=_("uid"), max_length=app_settings.UID_MAX_LENGTH ) last_login = models.DateTimeField(verbose_name=_("last login"), auto_now=True) date_joined = models.DateTimeField(verbose_name=_("date joined"), auto_now_add=True) extra_data = models.JSONField(verbose_name=_("extra data"), default=dict) class Meta: unique_together = ("provider", "uid") verbose_name = _("social account") verbose_name_plural = _("social accounts") def authenticate(self): return authenticate(account=self) def __str__(self) -> str: from .helpers import socialaccount_user_display return socialaccount_user_display(self) def get_profile_url(self): return self.get_provider_account().get_profile_url() def get_avatar_url(self): return self.get_provider_account().get_avatar_url() def get_provider(self, request=None): provider = getattr(self, "_provider", None) if provider: return provider adapter = get_adapter() provider = self._provider = adapter.get_provider( request or context.request, provider=self.provider ) return provider def get_provider_account(self): return self.get_provider().wrap_account(self) class SocialToken(models.Model): app = models.ForeignKey(SocialApp, on_delete=models.SET_NULL, blank=True, null=True) account = models.ForeignKey(SocialAccount, on_delete=models.CASCADE) token = models.TextField( verbose_name=_("token"), help_text=_('"oauth_token" (OAuth1) or access token (OAuth2)'), ) token_secret = models.TextField( blank=True, verbose_name=_("token secret"), help_text=_('"oauth_token_secret" (OAuth1) or refresh token (OAuth2)'), ) expires_at = models.DateTimeField( blank=True, null=True, verbose_name=_("expires at") ) class Meta: unique_together = ("app", "account") verbose_name = _("social application token") verbose_name_plural = _("social application tokens") def __str__(self) -> str: return f"{self._meta.verbose_name} ({self.pk})" class SocialLogin: """ Represents a social user that is in the process of being logged in. This consists of the following information: `account` (`SocialAccount` instance): The social account being logged in. Providers are not responsible for checking whether or not an account already exists or not. Therefore, a provider typically creates a new (unsaved) `SocialAccount` instance. The `User` instance pointed to by the account (`account.user`) may be prefilled by the provider for use as a starting point later on during the signup process. `token` (`SocialToken` instance): An optional access token token that results from performing a successful authentication handshake. `state` (`dict`): The state to be preserved during the authentication handshake. Note that this state may end up in the url -- do not put any secrets in here. It currently only contains the url to redirect to after login. `email_addresses` (list of `EmailAddress`): Optional list of email addresses retrieved from the provider. """ account: SocialAccount token: SocialToken | None email_addresses: list[EmailAddress] state: dict _did_authenticate_by_email: str | None phone: str | None phone_verified: bool def __init__( self, user=None, account: SocialAccount | None = None, token: SocialToken | None = None, email_addresses: list[EmailAddress] | None = None, provider=None, phone: str | None = None, phone_verified: bool = False, ) -> None: self.provider = provider if token: assert token.account is None or token.account == account # nosec self.token = token self.user = user if account: self.account = account self.email_addresses = email_addresses if email_addresses else [] self.state = {} self.phone = phone self.phone_verified = phone_verified def connect(self, request, user) -> None: self.user = user self.save(request, connect=True) signals.social_account_added.send( sender=SocialLogin, request=request, sociallogin=self ) get_adapter().send_notification_mail( "socialaccount/email/account_connected", self.user, context={ "account": self.account, "provider": self.account.get_provider(), }, ) @property def is_headless(self) -> bool: return bool(self.state.get("headless")) def serialize(self) -> dict[str, Any]: serialize_instance = get_adapter().serialize_instance ret = dict( provider=self.provider.serialize(), account=serialize_instance(self.account), user=serialize_instance(self.user), state=self.state, email_addresses=[serialize_instance(ea) for ea in self.email_addresses], phone=self.phone, phone_verified=self.phone_verified, ) if self.token: ret["token"] = serialize_instance(self.token) return ret @classmethod def deserialize(cls, data: dict[str, Any]) -> "SocialLogin": from allauth.socialaccount.providers.base.provider import Provider if not isinstance(data, dict): raise ValueError() deserialize_instance = get_adapter().deserialize_instance provider = Provider.deserialize(data.get("provider")) account = deserialize_instance(SocialAccount, data.get("account")) user = deserialize_instance(get_user_model(), data.get("user")) if token_data := data.get("token"): token = deserialize_instance(SocialToken, token_data) else: token = None email_addresses = [] if eas := data.get("email_addresses"): for ea in eas: email_address = deserialize_instance(EmailAddress, ea) email_addresses.append(email_address) ret = cls() ret.provider = provider ret.token = token ret.account = account ret.user = user ret.email_addresses = email_addresses ret.state = data.get("state") or {} ret.phone = data.get("phone") ret.phone_verified = data.get("phone_verified", False) return ret def save(self, request, connect: bool = False) -> None: """ Saves a new account. Note that while the account is new, the user may be an existing one (when connecting accounts) """ user = self.user user.save() self.account.user = user self.account.save() if app_settings.STORE_TOKENS and self.token: self.token.account = self.account self.token.save() if connect: # TODO: Add any new email addresses automatically? pass else: setup_user_email(request, user, self.email_addresses) if self.phone: account_adapter = get_account_adapter() if account_adapter._has_phone_impl: account_adapter.set_phone(user, self.phone, self.phone_verified) @property def is_existing(self) -> bool: """When `False`, this social login represents a temporary account, not yet backed by a database record. """ if self.user.pk is None: return False return get_user_model().objects.filter(pk=self.user.pk).exists() def lookup(self) -> None: """Look up the existing local user account to which this social login points, if any. """ self._did_authenticate_by_email = None if not self._lookup_by_socialaccount(): self._lookup_by_email() def _lookup_by_socialaccount(self) -> bool: assert not self.is_existing # nosec try: a = SocialAccount.objects.get( provider=self.account.provider, uid=self.account.uid ) # Update account a.extra_data = self.account.extra_data self.account = a self.user = self.account.user a.save() signals.social_account_updated.send( sender=SocialLogin, request=context.request, sociallogin=self ) self._store_token() return True except SocialAccount.DoesNotExist: return False def _store_token(self) -> None: # Update token if not app_settings.STORE_TOKENS or not self.token: return assert not self.token.pk # nosec app = self.token.app if app and not app.pk: # If the app is not stored in the db, leave the FK empty. app = None try: t = SocialToken.objects.get(account=self.account, app=app) t.token = self.token.token if self.token.token_secret: # only update the refresh token if we got one # many oauth2 providers do not resend the refresh token t.token_secret = self.token.token_secret t.expires_at = self.token.expires_at t.save() self.token = t except SocialToken.DoesNotExist: self.token.account = self.account self.token.app = app self.token.save() def _lookup_by_email(self) -> None: emails = [e.email for e in self.email_addresses if e.verified] for email in emails: if not get_adapter().can_authenticate_by_email(self, email): continue users = filter_users_by_email(email, prefer_verified=True) if users: self.user = users[0] self._did_authenticate_by_email = email return def _accept_login(self, request) -> None: from allauth.socialaccount.internal.flows.email_authentication import ( wipe_password, ) if self._did_authenticate_by_email: wipe_password(request, self.user, self._did_authenticate_by_email) if app_settings.EMAIL_AUTHENTICATION_AUTO_CONNECT: self.connect(context.request, self.user) def get_redirect_url(self, request) -> str | None: url = self.state.get("next") return url @classmethod def state_from_request(cls, request) -> dict[str, Any]: """ TODO: Deprecated! To be integrated with provider.redirect() """ state = {} next_url = get_next_redirect_url(request) if next_url: state["next"] = next_url state["process"] = get_request_param(request, "process", "login") state["scope"] = get_request_param(request, "scope", "") state["auth_params"] = get_request_param(request, "auth_params", "") return state @classmethod def stash_state(cls, request, state: dict[str, Any] | None = None) -> str: if state is None: # Only for providers that don't support redirect() yet. state = cls.state_from_request(request) return statekit.stash_state(request, state) @classmethod def unstash_state(cls, request) -> dict[str, Any] | None: state = statekit.unstash_last_state(request) if state is None: raise PermissionDenied() return state ================================================ FILE: allauth/socialaccount/providers/__init__.py ================================================ import importlib from collections import OrderedDict from django.apps import apps from django.conf import settings from allauth.utils import import_attribute class ProviderRegistry: def __init__(self): self.provider_map = OrderedDict() self.loaded = False def get_class_list(self): self.load() return list(self.provider_map.values()) def register(self, cls): self.provider_map[cls.id] = cls def get_class(self, id): return self.provider_map.get(id) def as_choices(self): self.load() for provider_cls in self.provider_map.values(): yield (provider_cls.id, provider_cls.name) def load(self): # TODO: Providers register with the provider registry when # loaded. Here, we build the URLs for all registered providers. So, we # really need to be sure all providers did register, which is why we're # forcefully importing the `provider` modules here. The overall # mechanism is way to magical and depends on the import order et al, so # all of this really needs to be revisited. if not self.loaded: for app_config in apps.get_app_configs(): try: module_name = f"{app_config.name}.provider" provider_module = importlib.import_module(module_name) except ImportError as e: if e.name != module_name: raise else: provider_settings = getattr(settings, "SOCIALACCOUNT_PROVIDERS", {}) for cls in getattr(provider_module, "provider_classes", []): provider_class = provider_settings.get(cls.id, {}).get( "provider_class" ) if provider_class: cls = import_attribute(provider_class) self.register(cls) self.loaded = True registry = ProviderRegistry() ================================================ FILE: allauth/socialaccount/providers/agave/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/agave/provider.py ================================================ from allauth.socialaccount.providers.agave.views import AgaveAdapter from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class AgaveAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("web_url", "dflt") def get_avatar_url(self): return self.account.extra_data.get("avatar_url", "dflt") class AgaveProvider(OAuth2Provider): id = "agave" name = "Agave" account_class = AgaveAccount oauth2_adapter_class = AgaveAdapter def extract_uid(self, data): return str(data.get("create_time")) def extract_common_fields(self, data): return dict( email=data.get("email"), username=data.get("username", ""), name=(f"{data.get('first_name', '')} {data.get('last_name', '')}".strip()), ) def get_default_scope(self): scope = ["PRODUCTION"] return scope provider_classes = [AgaveProvider] ================================================ FILE: allauth/socialaccount/providers/agave/urls.py ================================================ from allauth.socialaccount.providers.agave.provider import AgaveProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(AgaveProvider) ================================================ FILE: allauth/socialaccount/providers/agave/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class AgaveAdapter(OAuth2Adapter): provider_id = "agave" settings = app_settings.PROVIDERS.get(provider_id, {}) provider_base_url = settings.get("API_URL", "https://public.agaveapi.co") access_token_url = f"{provider_base_url}/token" authorize_url = f"{provider_base_url}/authorize" profile_url = f"{provider_base_url}/profiles/v2/me" def complete_login(self, request, app, token, response): with get_adapter().get_requests_session() as sess: extra_data = sess.get( self.profile_url, params={"access_token": token.token}, headers={"Authorization": f"Bearer {token.token}"}, ) user_profile = extra_data.json().get("result", {}) return self.get_provider().sociallogin_from_response(request, user_profile) oauth2_login = OAuth2LoginView.adapter_view(AgaveAdapter) oauth2_callback = OAuth2CallbackView.adapter_view(AgaveAdapter) ================================================ FILE: allauth/socialaccount/providers/amazon/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/amazon/provider.py ================================================ from allauth.socialaccount.providers.amazon.views import AmazonOAuth2Adapter from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class AmazonAccount(ProviderAccount): pass class AmazonProvider(OAuth2Provider): id = "amazon" name = "Amazon" account_class = AmazonAccount oauth2_adapter_class = AmazonOAuth2Adapter def get_default_scope(self): return ["profile"] def extract_uid(self, data): return str(data["user_id"]) def extract_common_fields(self, data): # Hackish way of splitting the fullname. # Assumes no middlenames. name = data.get("name", "") first_name, last_name = name, "" if name and " " in name: first_name, last_name = name.split(" ", 1) return dict( email=data.get("email", ""), last_name=last_name, first_name=first_name ) provider_classes = [AmazonProvider] ================================================ FILE: allauth/socialaccount/providers/amazon/urls.py ================================================ from allauth.socialaccount.providers.amazon.provider import AmazonProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(AmazonProvider) ================================================ FILE: allauth/socialaccount/providers/amazon/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class AmazonOAuth2Adapter(OAuth2Adapter): provider_id = "amazon" access_token_url = "https://api.amazon.com/auth/o2/token" # nosec authorize_url = "https://www.amazon.com/ap/oa" profile_url = "https://api.amazon.com/user/profile" def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: response = sess.get(self.profile_url, params={"access_token": token.token}) response.raise_for_status() extra_data = response.json() if "Profile" in extra_data: extra_data = { "user_id": extra_data["Profile"]["CustomerId"], "name": extra_data["Profile"]["Name"], "email": extra_data["Profile"]["PrimaryEmail"], } return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(AmazonOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(AmazonOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/amazon_cognito/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/amazon_cognito/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount.providers.amazon_cognito.utils import ( convert_to_python_bool_if_value_is_json_string_bool, ) from allauth.socialaccount.providers.amazon_cognito.views import ( AmazonCognitoOAuth2Adapter, ) from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class AmazonCognitoAccount(ProviderAccount): def get_avatar_url(self): return self.account.extra_data.get("picture") def get_profile_url(self): return self.account.extra_data.get("profile") class AmazonCognitoProvider(OAuth2Provider): id = "amazon_cognito" name = "Amazon Cognito" account_class = AmazonCognitoAccount oauth2_adapter_class = AmazonCognitoOAuth2Adapter def extract_uid(self, data): return str(data["sub"]) def extract_common_fields(self, data): return { "email": data.get("email"), "first_name": data.get("given_name"), "last_name": data.get("family_name"), } def get_default_scope(self): return ["openid", "profile", "email"] def extract_email_addresses(self, data): email = data.get("email") verified = convert_to_python_bool_if_value_is_json_string_bool( data.get("email_verified", False) ) return ( [EmailAddress(email=email, verified=verified, primary=True)] if email else [] ) def extract_extra_data(self, data): ret = dict(data) phone_number_verified = data.get("phone_number_verified") if phone_number_verified is not None: ret["phone_number_verified"] = ( convert_to_python_bool_if_value_is_json_string_bool( "phone_number_verified" ) ) return ret @classmethod def get_slug(cls): # IMPORTANT: Amazon Cognito does not support `_` characters # as part of their redirect URI. return super().get_slug().replace("_", "-") provider_classes = [AmazonCognitoProvider] ================================================ FILE: allauth/socialaccount/providers/amazon_cognito/urls.py ================================================ from allauth.socialaccount.providers.amazon_cognito.provider import ( AmazonCognitoProvider, ) from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(AmazonCognitoProvider) ================================================ FILE: allauth/socialaccount/providers/amazon_cognito/utils.py ================================================ def convert_to_python_bool_if_value_is_json_string_bool(s): if s == "true": return True elif s == "false": return False return s ================================================ FILE: allauth/socialaccount/providers/amazon_cognito/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.models import SocialToken from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class AmazonCognitoOAuth2Adapter(OAuth2Adapter): provider_id = "amazon_cognito" DOMAIN_KEY_MISSING_ERROR = ( '"DOMAIN" key is missing in Amazon Cognito configuration.' ) @property def settings(self): return app_settings.PROVIDERS.get(self.provider_id, {}) @property def domain(self): domain = self.settings.get("DOMAIN") if domain is None: raise ValueError(self.DOMAIN_KEY_MISSING_ERROR) return domain @property def access_token_url(self): return f"{self.domain}/oauth2/token" @property def authorize_url(self): return f"{self.domain}/oauth2/authorize" @property def profile_url(self): return f"{self.domain}/oauth2/userInfo" def complete_login(self, request, app, token: SocialToken, **kwargs): headers = { "Authorization": f"Bearer {token.token}", } with get_adapter().get_requests_session() as sess: response = sess.get(self.profile_url, headers=headers) response.raise_for_status() extra_data = response.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(AmazonCognitoOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(AmazonCognitoOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/angellist/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/angellist/provider.py ================================================ from allauth.socialaccount.providers.angellist.views import AngelListOAuth2Adapter from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class AngelListAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("angellist_url") def get_avatar_url(self): return self.account.extra_data.get("image") class AngelListProvider(OAuth2Provider): id = "angellist" name = "AngelList" account_class = AngelListAccount oauth2_adapter_class = AngelListOAuth2Adapter def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict( email=data.get("email"), username=data.get("angellist_url").split("/")[-1], name=data.get("name"), ) provider_classes = [AngelListProvider] ================================================ FILE: allauth/socialaccount/providers/angellist/urls.py ================================================ from allauth.socialaccount.providers.angellist.provider import AngelListProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(AngelListProvider) ================================================ FILE: allauth/socialaccount/providers/angellist/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class AngelListOAuth2Adapter(OAuth2Adapter): provider_id = "angellist" access_token_url = "https://angel.co/api/oauth/token/" # nosec authorize_url = "https://angel.co/api/oauth/authorize/" profile_url = "https://api.angel.co/1/me/" supports_state = False def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, params={"access_token": token.token}) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(AngelListOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(AngelListOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/apple/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/apple/apple_session.py ================================================ from allauth.socialaccount.sessions import LoginSession APPLE_SESSION_COOKIE_NAME = "apple-login-session" def get_apple_session(request): return LoginSession(request, "apple_login_session", APPLE_SESSION_COOKIE_NAME) ================================================ FILE: allauth/socialaccount/providers/apple/client.py ================================================ import time from http import HTTPStatus from urllib.parse import parse_qsl, quote, urlencode from django.core.exceptions import ImproperlyConfigured import jwt from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Client, OAuth2Error def jwt_encode(*args, **kwargs): resp = jwt.encode(*args, **kwargs) if isinstance(resp, bytes): # For PyJWT <2 resp = resp.decode("utf-8") return resp class Scope: EMAIL = "email" NAME = "name" class AppleOAuth2Client(OAuth2Client): """ Custom client because `Sign In With Apple`: * requires `response_mode` field in redirect_url * requires special `client_secret` as JWT """ def generate_client_secret(self) -> str: """Create a JWT signed with an apple provided private key""" now = int(time.time()) app = get_adapter(self.request).get_app(self.request, "apple") if not app.key: raise ImproperlyConfigured("Apple 'key' missing") certificate_key = app.settings.get("certificate_key") if not certificate_key: raise ImproperlyConfigured("Apple 'certificate_key' missing") claims = { "iss": app.key, "aud": "https://appleid.apple.com", "sub": self.get_client_id(), "iat": now, "exp": now + 60 * 60, } headers = {"kid": self.consumer_secret, "alg": "ES256"} client_secret = jwt_encode( payload=claims, key=certificate_key, algorithm="ES256", headers=headers ) return client_secret def get_client_id(self) -> str: """We support multiple client_ids, but use the first one for api calls""" return self.consumer_key.split(",")[0] def get_access_token(self, code, pkce_code_verifier=None): url = self.access_token_url client_secret = self.generate_client_secret() data = { "client_id": self.get_client_id(), "code": code, "grant_type": "authorization_code", "redirect_uri": self.callback_url, "client_secret": client_secret, } if pkce_code_verifier: data["code_verifier"] = pkce_code_verifier self._strip_empty_keys(data) with get_adapter().get_requests_session() as sess: resp = sess.request( self.access_token_method, url, data=data, headers=self.headers ) access_token = None if resp.status_code in [HTTPStatus.OK, HTTPStatus.CREATED]: try: access_token = resp.json() except ValueError: access_token = dict(parse_qsl(resp.text)) if not access_token or "access_token" not in access_token: raise OAuth2Error(f"Error retrieving access token: {resp.content}") return access_token def get_redirect_url(self, authorization_url, scope, extra_params) -> str: scope = self.scope_delimiter.join(set(scope)) params = { "client_id": self.get_client_id(), "redirect_uri": self.callback_url, "response_mode": "form_post", "scope": scope, "response_type": "code id_token", } if self.state: params["state"] = self.state params.update(extra_params) return f"{authorization_url}?{urlencode(params, quote_via=quote)}" ================================================ FILE: allauth/socialaccount/providers/apple/provider.py ================================================ import requests from allauth.account.models import EmailAddress from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.app_settings import QUERY_EMAIL from allauth.socialaccount.providers.apple.views import AppleOAuth2Adapter from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.client import OAuth2Error from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class AppleAccount(ProviderAccount): def to_str(self): email = self.account.extra_data.get("email") if email and not email.lower().endswith("@privaterelay.appleid.com"): return email name = self.account.extra_data.get("name") or {} if name.get("firstName") or name.get("lastName"): full_name = f"{name['firstName'] or ''} {name['lastName'] or ''}" full_name = full_name.strip() if full_name: return full_name return super().to_str() class AppleProvider(OAuth2Provider): id = "apple" name = "Apple" account_class = AppleAccount oauth2_adapter_class = AppleOAuth2Adapter supports_token_authentication = True def extract_uid(self, data): return str(data["sub"]) def extract_common_fields(self, data): fields = {"email": data.get("email")} # If the name was provided name = data.get("name") if name: fields["first_name"] = name.get("firstName", "") fields["last_name"] = name.get("lastName", "") return fields def extract_email_addresses(self, data): ret = [] email = data.get("email") verified = data.get("email_verified") if isinstance(verified, str): verified = verified.lower() == "true" if email: ret.append( EmailAddress( email=email, verified=verified, primary=True, ) ) return ret def get_default_scope(self): scopes = ["name"] if QUERY_EMAIL: scopes.append("email") return scopes def verify_token(self, request, token): from allauth.socialaccount.providers.apple.views import AppleOAuth2Adapter id_token = token.get("id_token") if not id_token: raise get_adapter().validation_error("invalid_token") try: identity_data = AppleOAuth2Adapter.get_verified_identity_data( self, id_token ) except (OAuth2Error, requests.RequestException) as e: raise get_adapter().validation_error("invalid_token") from e login = self.sociallogin_from_response(request, identity_data) return login def get_auds(self): return [aud.strip() for aud in self.app.client_id.split(",")] provider_classes = [AppleProvider] ================================================ FILE: allauth/socialaccount/providers/apple/urls.py ================================================ from django.urls import path from allauth.socialaccount.providers.apple.provider import AppleProvider from allauth.socialaccount.providers.apple.views import oauth2_finish_login from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(AppleProvider) urlpatterns += [ path( f"{AppleProvider.get_slug()}/login/callback/finish/", oauth2_finish_login, name="apple_finish_callback", ), ] ================================================ FILE: allauth/socialaccount/providers/apple/views.py ================================================ import json from datetime import timedelta from django.http import HttpResponseNotAllowed, HttpResponseRedirect from django.urls import reverse from django.utils import timezone from django.utils.http import urlencode from django.views.decorators.csrf import csrf_exempt from allauth.account.internal.decorators import login_not_required from allauth.socialaccount.internal import jwtkit from allauth.socialaccount.models import SocialToken from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) from allauth.utils import build_absolute_uri, get_request_param from .apple_session import get_apple_session from .client import AppleOAuth2Client class AppleOAuth2Adapter(OAuth2Adapter): client_class = AppleOAuth2Client provider_id = "apple" access_token_url = "https://appleid.apple.com/auth/token" # nosec authorize_url = "https://appleid.apple.com/auth/authorize" public_key_url = "https://appleid.apple.com/auth/keys" @classmethod def get_verified_identity_data(cls, provider, id_token): data = jwtkit.verify_and_decode( credential=id_token, keys_url=cls.public_key_url, issuer="https://appleid.apple.com", audience=provider.get_auds(), lookup_kid=jwtkit.lookup_kid_jwk, ) return data def parse_token(self, data): token = SocialToken( token=data["access_token"], ) token.token_secret = data.get("refresh_token", "") expires_in = data.get(self.expires_in_key) if expires_in: token.expires_at = timezone.now() + timedelta(seconds=int(expires_in)) # `user_data` is a big flat dictionary with the parsed JWT claims # access_tokens, and user info from the apple post. identity_data = AppleOAuth2Adapter.get_verified_identity_data( self.get_provider(), data["id_token"] ) token.user_data = {**data, **identity_data} return token def complete_login(self, request, app, token, **kwargs): extra_data = token.user_data login = self.get_provider().sociallogin_from_response( request=request, response=extra_data ) login.state["id_token"] = token.user_data # We can safely remove the apple login session now # Note: The cookie will remain, but it's set to delete on browser close get_apple_session(request).delete() return login def get_user_scope_data(self, request): user_scope_data = request.apple_login_session.get("user", "") try: return json.loads(user_scope_data) except json.JSONDecodeError: # We do not care much about user scope data as it maybe blank # so return blank dictionary instead return {} def get_access_token_data(self, request, app, client, pkce_code_verifier=None): """We need to gather the info from the apple specific login""" apple_session = get_apple_session(request) # Exchange `code` code = get_request_param(request, "code") access_token_data = client.get_access_token( code, pkce_code_verifier=pkce_code_verifier ) id_token = access_token_data.get("id_token", None) # In case of missing id_token in access_token_data if id_token is None: id_token = apple_session.store.get("id_token") return { **access_token_data, **self.get_user_scope_data(request), "id_token": id_token, } @csrf_exempt @login_not_required def apple_post_callback(request, finish_endpoint_name="apple_finish_callback"): """ Apple uses a `form_post` response type, which due to CORS/Samesite-cookie rules means this request cannot access the request since the session cookie is unavailable. We work around this by storing the apple response in a separate, temporary session and redirecting to a more normal oauth flow. args: finish_endpoint_name (str): The name of a defined URL, which can be overridden in your url configuration if you have more than one callback endpoint. """ if request.method != "POST": return HttpResponseNotAllowed(["POST"]) apple_session = get_apple_session(request) # Add regular OAuth2 params to the URL - reduces the overrides required keys_to_put_in_url = ["code", "state", "error"] url_params = {} for key in keys_to_put_in_url: value = get_request_param(request, key, "") if value: url_params[key] = value # Add other params to the apple_login_session keys_to_save_to_session = ["user", "id_token"] for key in keys_to_save_to_session: apple_session.store[key] = get_request_param(request, key, "") url = build_absolute_uri(request, reverse(finish_endpoint_name)) response = HttpResponseRedirect(f"{url}?{urlencode(url_params)}") apple_session.save(response) return response oauth2_login = OAuth2LoginView.adapter_view(AppleOAuth2Adapter) oauth2_callback = apple_post_callback oauth2_finish_login = OAuth2CallbackView.adapter_view(AppleOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/asana/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/asana/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/asana/provider.py ================================================ from allauth.socialaccount.providers.asana.views import AsanaOAuth2Adapter from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class AsanaAccount(ProviderAccount): pass class AsanaProvider(OAuth2Provider): id = "asana" name = "Asana" account_class = AsanaAccount oauth2_adapter_class = AsanaOAuth2Adapter def extract_uid(self, data): if "gid" not in data: # `id` is legacy: https://developers.asana.com/reference/getuser return str(data["id"]) return str(data["gid"]) def extract_common_fields(self, data): return dict(email=data.get("email"), name=data.get("name")) provider_classes = [AsanaProvider] ================================================ FILE: allauth/socialaccount/providers/asana/urls.py ================================================ from allauth.socialaccount.providers.asana.provider import AsanaProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(AsanaProvider) ================================================ FILE: allauth/socialaccount/providers/asana/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class AsanaOAuth2Adapter(OAuth2Adapter): provider_id = "asana" access_token_url = "https://app.asana.com/-/oauth_token" # nosec authorize_url = "https://app.asana.com/-/oauth_authorize" profile_url = "https://app.asana.com/api/1.0/users/me" def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, params={"access_token": token.token}) extra_data = resp.json()["data"] return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(AsanaOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(AsanaOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/atlassian/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/atlassian/provider.py ================================================ from allauth.socialaccount.providers.atlassian.views import AtlassianOAuth2Adapter from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class AtlassianAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("picture") class AtlassianProvider(OAuth2Provider): id = "atlassian" name = "Atlassian" account_class = AtlassianAccount oauth2_adapter_class = AtlassianOAuth2Adapter def extract_uid(self, data): return data["account_id"] def extract_common_fields(self, data): return { "email": data.get("email"), "name": data.get("name"), "username": data.get("nickname"), "email_verified": data.get("email_verified"), } def get_default_scope(self): return ["read:me"] def get_auth_params(self): params = super().get_auth_params() params.update({"audience": "api.atlassian.com", "prompt": "consent"}) return params provider_classes = [AtlassianProvider] ================================================ FILE: allauth/socialaccount/providers/atlassian/urls.py ================================================ from allauth.socialaccount.providers.atlassian.provider import AtlassianProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(AtlassianProvider) ================================================ FILE: allauth/socialaccount/providers/atlassian/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.models import SocialToken from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class AtlassianOAuth2Adapter(OAuth2Adapter): provider_id = "atlassian" access_token_url = "https://api.atlassian.com/oauth/token" # nosec authorize_url = "https://auth.atlassian.com/authorize" profile_url = "https://api.atlassian.com/me" def complete_login(self, request, app, token: SocialToken, **kwargs): headers = { "Authorization": f"Bearer {token.token}", "Accept": "application/json", } with get_adapter().get_requests_session() as sess: response = sess.get(self.profile_url, headers=headers) response.raise_for_status() data = response.json() return self.get_provider().sociallogin_from_response(request, data) oauth2_login = OAuth2LoginView.adapter_view(AtlassianOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(AtlassianOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/auth0/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/auth0/provider.py ================================================ from allauth.socialaccount.providers.auth0.views import Auth0OAuth2Adapter from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class Auth0Account(ProviderAccount): def get_avatar_url(self): return self.account.extra_data.get("picture") class Auth0Provider(OAuth2Provider): id = "auth0" name = "Auth0" account_class = Auth0Account oauth2_adapter_class = Auth0OAuth2Adapter def get_default_scope(self): return ["openid", "profile", "email"] def extract_uid(self, data): return str(data["sub"]) def extract_common_fields(self, data): return dict( email=data.get("email"), username=data.get("username"), name=data.get("name"), ) provider_classes = [Auth0Provider] ================================================ FILE: allauth/socialaccount/providers/auth0/urls.py ================================================ from allauth.socialaccount.providers.auth0.provider import Auth0Provider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(Auth0Provider) ================================================ FILE: allauth/socialaccount/providers/auth0/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class Auth0OAuth2Adapter(OAuth2Adapter): provider_id = "auth0" settings = app_settings.PROVIDERS.get(provider_id, {}) provider_base_url = settings.get("AUTH0_URL") access_token_url = f"{provider_base_url}/oauth/token" authorize_url = f"{provider_base_url}/authorize" profile_url = f"{provider_base_url}/userinfo" def complete_login(self, request, app, token, response): with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, params={"access_token": token.token}) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(Auth0OAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(Auth0OAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/authentiq/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/authentiq/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount import app_settings from allauth.socialaccount.providers.authentiq.views import AuthentiqOAuth2Adapter from allauth.socialaccount.providers.base import AuthAction, ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class Scope: NAME = "aq:name" EMAIL = "email" PHONE = "phone" ADDRESS = "address" LOCATION = "aq:location" PUSH = "aq:push" IDENTITY_CLAIMS = frozenset( [ "sub", "name", "given_name", "family_name", "middle_name", "nickname", "preferred_username", "profile", "picture", "website", "email", "email_verified", "gender", "birthdate", "zoneinfo", "locale", "phone_number", "phone_number_verified", "address", "updated_at", "aq:location", ] ) class AuthentiqAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("profile") def get_avatar_url(self): return self.account.extra_data.get("picture") class AuthentiqProvider(OAuth2Provider): id = "authentiq" name = "Authentiq" account_class = AuthentiqAccount oauth2_adapter_class = AuthentiqOAuth2Adapter def get_scope_from_request(self, request): scope = set(super().get_scope_from_request(request)) scope.add("openid") if Scope.EMAIL in scope: modifiers = "" if app_settings.EMAIL_REQUIRED: modifiers += "r" if app_settings.EMAIL_VERIFICATION: modifiers += "s" if modifiers: scope.add(f"{Scope.EMAIL}~{modifiers}") scope.remove(Scope.EMAIL) return list(scope) def get_default_scope(self): scope = [Scope.NAME, Scope.PUSH] if app_settings.QUERY_EMAIL: scope.append(Scope.EMAIL) return scope def get_auth_params_from_request(self, request, action): ret = super().get_auth_params_from_request(request, action) if action == AuthAction.REAUTHENTICATE: ret["prompt"] = "select_account" return ret def extract_uid(self, data): return str(data["sub"]) def extract_common_fields(self, data): return dict( username=data.get("preferred_username", data.get("given_name")), email=data.get("email"), name=data.get("name"), first_name=data.get("given_name"), last_name=data.get("family_name"), ) def extract_extra_data(self, data): return {k: v for k, v in data.items() if k in IDENTITY_CLAIMS} def extract_email_addresses(self, data): ret = [] email = data.get("email") if email and data.get("email_verified"): ret.append(EmailAddress(email=email, verified=True, primary=True)) return ret provider_classes = [AuthentiqProvider] ================================================ FILE: allauth/socialaccount/providers/authentiq/urls.py ================================================ from allauth.socialaccount.providers.authentiq.provider import AuthentiqProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(AuthentiqProvider) ================================================ FILE: allauth/socialaccount/providers/authentiq/views.py ================================================ from urllib.parse import urljoin from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class AuthentiqOAuth2Adapter(OAuth2Adapter): provider_id = "authentiq" settings = app_settings.PROVIDERS.get(provider_id, {}) provider_url = settings.get("PROVIDER_URL", "https://connect.authentiq.io/") if not provider_url.endswith("/"): provider_url += "/" access_token_url = urljoin(provider_url, "token") authorize_url = urljoin(provider_url, "authorize") profile_url = urljoin(provider_url, "userinfo") def complete_login(self, request, app, token, **kwargs): auth = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=auth) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(AuthentiqOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(AuthentiqOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/baidu/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/baidu/provider.py ================================================ from allauth.socialaccount.providers.baidu.views import BaiduOAuth2Adapter from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class BaiduAccount(ProviderAccount): def get_profile_url(self): return f"https://www.baidu.com/p/{self.account.extra_data.get('uname')}" def get_avatar_url(self): portrait = self.account.extra_data.get("portrait") return f"https://tb.himg.baidu.com/sys/portraitn/item/{portrait}" def to_str(self): dflt = super().to_str() return self.account.extra_data.get("uname", dflt) class BaiduProvider(OAuth2Provider): id = "baidu" name = "Baidu" account_class = BaiduAccount oauth2_adapter_class = BaiduOAuth2Adapter def extract_uid(self, data): return data["uid"] def extract_common_fields(self, data): return dict(username=data.get("uid"), name=data.get("uname")) provider_classes = [BaiduProvider] ================================================ FILE: allauth/socialaccount/providers/baidu/urls.py ================================================ from allauth.socialaccount.providers.baidu.provider import BaiduProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(BaiduProvider) ================================================ FILE: allauth/socialaccount/providers/baidu/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class BaiduOAuth2Adapter(OAuth2Adapter): provider_id = "baidu" access_token_url = "https://openapi.baidu.com/oauth/2.0/token" # nosec authorize_url = "https://openapi.baidu.com/oauth/2.0/authorize" profile_url = ( "https://openapi.baidu.com/rest/2.0/passport/users/getLoggedInUser" # noqa ) def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, params={"access_token": token.token}) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(BaiduOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(BaiduOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/base/__init__.py ================================================ from allauth.socialaccount.providers.base.provider import Provider # noqa from allauth.socialaccount.providers.base.provider import ProviderAccount # noqa from allauth.socialaccount.providers.base.provider import ProviderException # noqa from .constants import AuthAction, AuthError, AuthProcess # noqa ================================================ FILE: allauth/socialaccount/providers/base/constants.py ================================================ class AuthProcess: LOGIN = "login" CONNECT = "connect" REDIRECT = "redirect" class AuthAction: AUTHENTICATE = "authenticate" REAUTHENTICATE = "reauthenticate" REREQUEST = "rerequest" class AuthError: UNKNOWN = "unknown" CANCELLED = "cancelled" # Cancelled on request of user DENIED = "denied" # Denied by server ================================================ FILE: allauth/socialaccount/providers/base/provider.py ================================================ from typing import Any from django.core.exceptions import ( ImproperlyConfigured, PermissionDenied, ValidationError, ) from django.http import HttpResponse from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.internal.emailkit import valid_email_or_none from allauth.account.utils import get_next_redirect_url, get_request_param from allauth.core import context from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.internal import statekit from allauth.socialaccount.providers.base.constants import AuthProcess class ProviderException(Exception): pass class Provider: name: str # Provided by subclasses id: str # Provided by subclasses slug: str | None = None # Provided by subclasses uses_apps = True supports_redirect = False # Indicates whether or not this provider supports logging in by posting an # access/id-token. supports_token_authentication = False def __init__(self, request, app=None) -> None: self.request = request if self.uses_apps and app is None: raise ValueError("missing: app") self.app = app def __str__(self) -> str: return self.name @classmethod def get_slug(cls) -> str: return cls.slug or cls.id def get_login_url(self, request, next=None, **kwargs) -> str: """ Builds the URL to redirect to when initiating a login for this provider. """ raise NotImplementedError(f"get_login_url() for {self.name}") def redirect_from_request(self, request) -> HttpResponse: kwargs = self.get_redirect_from_request_kwargs(request) return self.redirect(request, **kwargs) def get_redirect_from_request_kwargs(self, request) -> dict: kwargs = {} next_url = get_next_redirect_url(request) if next_url: kwargs["next_url"] = next_url kwargs["process"] = get_request_param(request, "process", AuthProcess.LOGIN) return kwargs def redirect( self, request, process, next_url=None, data=None, **kwargs ) -> HttpResponse: """ Initiate a redirect to the provider. """ raise NotImplementedError() def verify_token(self, request, token): """ Verifies the token, returning a `SocialLogin` instance when valid. Raises a `ValidationError` otherwise. """ raise NotImplementedError() def media_js(self, request) -> str: """ Some providers may require extra scripts (e.g. a Facebook connect) """ return "" def wrap_account(self, social_account): return self.account_class(social_account) def get_settings(self) -> dict: return app_settings.PROVIDERS.get(self.id, {}) def sociallogin_from_response(self, request, response): """ Instantiates and populates a `SocialLogin` model based on the data retrieved in `response`. The method does NOT save the model to the DB. Data for `SocialLogin` will be extracted from `response` with the help of the `.extract_uid()`, `.extract_extra_data()`, `.extract_common_fields()`, and `.extract_email_addresses()` methods. :param request: a Django `HttpRequest` object. :param response: object retrieved via the callback response of the social auth provider. :return: A populated instance of the `SocialLogin` model (unsaved). """ # NOTE: Avoid loading models at top due to registry boot... from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.models import SocialAccount, SocialLogin adapter = get_adapter() uid = self.extract_uid(response) if not isinstance(uid, str): raise ValueError(f"uid must be a string: {repr(uid)}") if len(uid) > app_settings.UID_MAX_LENGTH: raise ImproperlyConfigured( f"SOCIALACCOUNT_UID_MAX_LENGTH too small (<{len(uid)})" ) if not uid: raise ValueError("uid must be a non-empty string") extra_data = self.extract_extra_data(response) common_fields = self.extract_common_fields(response) socialaccount = SocialAccount( extra_data=extra_data, uid=uid, provider=self.sub_id, ) email_addresses = self.extract_email_addresses(response) email = self.cleanup_email_addresses( common_fields.get("email"), email_addresses, email_verified=bool(common_fields.get("email_verified")), ) if email: common_fields["email"] = email else: common_fields.pop("email", None) phone = common_fields.get("phone") if phone: try: phone = ( get_account_adapter().phone_form_field(required=True).clean(phone) ) common_fields["phone"] = phone except ValidationError: phone = None common_fields.pop("phone") sociallogin = SocialLogin( provider=self, account=socialaccount, email_addresses=email_addresses, phone=phone, phone_verified=common_fields.get("phone_verified", False), ) user = sociallogin.user = adapter.new_user(request, sociallogin) user.set_unusable_password() adapter.populate_user(request, sociallogin, common_fields) return sociallogin def extract_uid(self, data) -> str: """ Extracts the unique user ID from `data` """ raise NotImplementedError( "The provider must implement the `extract_uid()` method" ) def extract_extra_data(self, data) -> dict: """ Extracts fields from `data` that will be stored in `SocialAccount`'s `extra_data` JSONField, such as email address, first name, last name, and phone number. :return: any JSON-serializable Python structure. """ return data def extract_common_fields(self, data) -> dict: """ Extracts fields from `data` that will be used to populate the `User` model in the `SOCIALACCOUNT_ADAPTER`'s `populate_user()` method. For example: {'first_name': 'John'} :return: dictionary of key-value pairs. """ return {} def cleanup_email_addresses( self, email: str | None, addresses: list, email_verified: bool = False ) -> str | None: # Avoid loading models before adapters have been registered. from allauth.account.models import EmailAddress # Validate & clean the email addresses. email = valid_email_or_none(email) # A bit ugly, but the signature of this function is such that we have to # modify addresses in place. for idx in range(len(addresses))[::-1]: address = addresses[idx] address.email = valid_email_or_none(address.email) if not address.email: addresses.pop(idx) # Move user.email over to EmailAddress if email and email not in [a.email for a in addresses]: addresses.insert( 0, EmailAddress(email=email, verified=bool(email_verified), primary=True), ) # Force verified emails adapter = get_adapter() for address in addresses: if adapter.is_email_verified(self, address.email): address.verified = True # Sort in order of importance (primary, verified...) addresses.sort(key=lambda a: (a.primary, a.verified, a.email), reverse=True) if not email and addresses: email = addresses[0].email return email def extract_email_addresses(self, data) -> list: """ For example: [EmailAddress(email='john@example.com', verified=True, primary=True)] """ return [] @classmethod def get_package(cls) -> str: pkg = getattr(cls, "package", None) if not pkg: pkg = cls.__module__.rpartition(".")[0] return pkg def stash_redirect_state( self, request, process, next_url=None, data=None, state_id=None, **kwargs ): """ Stashes state, returning a (random) state ID using which the state can be looked up later. Application specific state is stored separately from (core) allauth state such as `process` and `**kwargs`. """ state = {"process": process, "data": data, **kwargs} if next_url: state["next"] = next_url return statekit.stash_state(request, state, state_id=state_id) def unstash_redirect_state(self, request, state_id): state = statekit.unstash_state(request, state_id) if state is None: raise PermissionDenied() return state @property def sub_id(self) -> str: return ( (self.app.provider_id or self.app.provider) if self.uses_apps else self.id ) def serialize(self) -> dict[str, Any]: ret = {"id": self.id} if self.uses_apps: ret["app.client_id"] = self.app.client_id return ret @classmethod def deserialize(cls, data: Any) -> "Provider": if not isinstance(data, dict): raise ValueError() provider = data.get("id") if not isinstance(provider, str): raise ValueError() client_id = data.get("app.client_id") if client_id is not None and not isinstance(client_id, str): raise ValueError() return get_adapter().get_provider( context.request, provider=provider, client_id=client_id, ) class ProviderAccount: def __init__(self, social_account): self.account = social_account def get_profile_url(self) -> str | None: return None def get_avatar_url(self) -> str | None: return None def get_brand(self) -> dict: """ Returns a dict containing an id and name identifying the brand. Useful when displaying logos next to accounts in templates. For most providers, these are identical to the provider. For OpenID however, the brand can derived from the OpenID identity url. """ provider = self.account.get_provider() return dict(id=provider.id, name=provider.name) def __str__(self) -> str: return self.to_str() def get_user_data(self) -> dict | None: """Typically, the ``extra_data`` directly contains user related keys. For some providers, however, they are nested below a different key. In that case, you can override this method so that the base ``__str__()`` will still be able to find the data. """ ret = self.account.extra_data if not isinstance(ret, dict): ret = None return ret def to_str(self) -> str: """ Returns string representation of this social account. This is the unique identifier of the account, such as its username or its email address. It should be meaningful to human beings, which means a numeric ID number is rarely the appropriate representation here. Subclasses are meant to override this method. Users will see the string representation of their social accounts in the page rendered by the allauth.socialaccount.views.connections view. The following code did not use to work in the past due to py2 compatibility: class GoogleAccount(ProviderAccount): def __str__(self): dflt = super(GoogleAccount, self).__str__() return self.account.extra_data.get('name', dflt) So we have this method `to_str` that can be overridden in a conventional fashion, without having to worry about it. """ user_data = self.get_user_data() if user_data: combi_values = {} tbl = [ # Prefer username -- it's the most human recognizable & unique. ( None, [ "username", "userName", "user_name", "login", "handle", ], ), # Second best is email (None, ["email", "Email", "mail", "email_address"]), ( None, [ "name", "display_name", "displayName", "displayname", "Display_Name", "nickname", ], ), # Use the full name (None, ["full_name", "fullName"]), # Alternatively, try to assemble a full name ourselves. ( "first_name", [ "first_name", "firstname", "firstName", "First_Name", "given_name", "givenName", ], ), ( "last_name", [ "last_name", "lastname", "lastName", "Last_Name", "family_name", "familyName", "surname", ], ), ] for store_as, variants in tbl: for key in variants: value = user_data.get(key) if isinstance(value, str): value = value.strip() if value and not store_as: return value combi_values[store_as] = value first_name = combi_values.get("first_name") or "" last_name = combi_values.get("last_name") or "" if first_name or last_name: return f"{first_name} {last_name}".strip() return self.get_brand()["name"] ================================================ FILE: allauth/socialaccount/providers/base/utils.py ================================================ from django.shortcuts import render from allauth.account import app_settings as account_app_settings from allauth.socialaccount import app_settings def respond_to_login_on_get(request, provider): if (not app_settings.LOGIN_ON_GET) and request.method == "GET": return render( request, f"socialaccount/login.{account_app_settings.TEMPLATE_EXTENSION}", { "provider": provider, "process": request.GET.get("process"), }, ) ================================================ FILE: allauth/socialaccount/providers/base/views.py ================================================ from django.http import Http404, HttpResponse from django.views import View from allauth import app_settings as allauth_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.base.utils import respond_to_login_on_get class BaseLoginView(View): provider_id: str # Set in subclasses def dispatch(self, request, *args, **kwargs) -> HttpResponse: if allauth_settings.HEADLESS_ONLY: raise Http404 provider = self.get_provider() resp = respond_to_login_on_get(request, provider) if resp: return resp return provider.redirect_from_request(request) def get_provider(self): provider = get_adapter().get_provider(self.request, self.provider_id) return provider ================================================ FILE: allauth/socialaccount/providers/basecamp/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/basecamp/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.basecamp.views import BasecampOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class BasecampAccount(ProviderAccount): def get_avatar_url(self): return None def get_user_data(self): return self.account.extra_data.get("identity", {}) class BasecampProvider(OAuth2Provider): id = "basecamp" name = "Basecamp" account_class = BasecampAccount oauth2_adapter_class = BasecampOAuth2Adapter def get_auth_params_from_request(self, request, action): data = super().get_auth_params_from_request(request, action) data["type"] = "web_server" return data def extract_uid(self, data): data = data["identity"] return str(data["id"]) def extract_common_fields(self, data): data = data["identity"] return dict( email=data.get("email_address"), username=data.get("email_address"), first_name=data.get("first_name"), last_name=data.get("last_name"), name=f"{data.get('first_name')} {data.get('last_name')}", ) provider_classes = [BasecampProvider] ================================================ FILE: allauth/socialaccount/providers/basecamp/urls.py ================================================ from allauth.socialaccount.providers.basecamp.provider import BasecampProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(BasecampProvider) ================================================ FILE: allauth/socialaccount/providers/basecamp/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class BasecampOAuth2Adapter(OAuth2Adapter): provider_id = "basecamp" access_token_url = ( "https://launchpad.37signals.com/authorization/token?type=web_server" # nosec ) authorize_url = "https://launchpad.37signals.com/authorization/new" profile_url = "https://launchpad.37signals.com/authorization.json" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(BasecampOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(BasecampOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/battlenet/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/battlenet/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/battlenet/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.battlenet.views import BattleNetOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class BattleNetAccount(ProviderAccount): def to_str(self): battletag = self.account.extra_data.get("battletag") return battletag or super().to_str() class BattleNetProvider(OAuth2Provider): id = "battlenet" name = "Battle.net" account_class = BattleNetAccount oauth2_adapter_class = BattleNetOAuth2Adapter def extract_uid(self, data): uid = str(data["id"]) if data.get("region") == "cn": # China is on a different account system. UIDs can clash with US. return f"{uid}-cn" return uid def extract_common_fields(self, data): return {"username": data.get("battletag")} def get_default_scope(self): # Optional scopes: "sc2.profile", "wow.profile" return [] provider_classes = [BattleNetProvider] ================================================ FILE: allauth/socialaccount/providers/battlenet/urls.py ================================================ from allauth.socialaccount.providers.battlenet.provider import BattleNetProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(BattleNetProvider) ================================================ FILE: allauth/socialaccount/providers/battlenet/validators.py ================================================ from django.core.validators import RegexValidator BattletagUsernameValidator = RegexValidator(r"^[\w.]+#\d+$") ================================================ FILE: allauth/socialaccount/providers/battlenet/views.py ================================================ """ OAuth2 Adapter for Battle.net Resources: * Battle.net OAuth2 documentation: https://dev.battle.net/docs/read/oauth * Battle.net API documentation: https://dev.battle.net/io-docs * Original announcement: https://us.battle.net/en/forum/topic/13979297799 * The Battle.net API forum: https://us.battle.net/en/forum/15051532/ """ from http import HTTPStatus from django.conf import settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Error from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class Region: APAC = "apac" CN = "cn" EU = "eu" KR = "kr" SEA = "sea" TW = "tw" US = "us" def _check_errors(response): try: data = response.json() except ValueError: # JSONDecodeError on py3 raise OAuth2Error(f"Invalid JSON from Battle.net API: {response.text!r}") if response.status_code >= HTTPStatus.BAD_REQUEST or "error" in data: # For errors, we expect the following format: # {"error": "error_name", "error_description": "Oops!"} # For example, if the token is not valid, we will get: # { # "error": "invalid_token", # "error_description": "Invalid access token: abcdef123456" # } # For the profile API, this may also look like the following: # {"code": 403, "type": "Forbidden", "detail": "Account Inactive"} error = data.get("error", "") or data.get("type", "") desc = data.get("error_description", "") or data.get("detail", "") raise OAuth2Error(f"Battle.net error: {error} ({desc})") # The expected output from the API follows this format: # {"id": 12345, "battletag": "Example#12345"} # The battletag is optional. if "id" not in data: # If the id is not present, the output is not usable (no UID) raise OAuth2Error(f"Invalid data from Battle.net API: {data!r}") return data class BattleNetOAuth2Adapter(OAuth2Adapter): """ OAuth2 adapter for Battle.net https://dev.battle.net/docs/read/oauth Region is set to us by default, but can be overridden with the `region` GET parameter when performing a login. Can be any of eu, us, kr, sea, tw or cn """ provider_id = "battlenet" valid_regions = ( Region.APAC, Region.CN, Region.EU, Region.KR, Region.SEA, Region.TW, Region.US, ) @property def battlenet_region(self): # Check by URI query parameter first. region = self.request.GET.get("region", "").lower() if region == Region.SEA: # South-East Asia uses the same region as US everywhere return Region.US if region in self.valid_regions: return region # Second, check the provider settings. region = ( getattr(settings, "SOCIALACCOUNT_PROVIDERS", {}) .get("battlenet", {}) .get("REGION", "us") ) if region in self.valid_regions: return region return Region.US @property def battlenet_base_url(self): region = self.battlenet_region if region == Region.CN: return "https://oauth.battlenet.com.cn" return "https://oauth.battle.net" @property def access_token_url(self): return f"{self.battlenet_base_url}/token" @property def authorize_url(self): return f"{self.battlenet_base_url}/authorize" @property def profile_url(self): return f"{self.battlenet_base_url}/userinfo" def complete_login(self, request, app, token, **kwargs): headers = {"authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: response = sess.get(self.profile_url, headers=headers) data = _check_errors(response) # Add the region to the data so that we can have it in `extra_data`. data["region"] = self.battlenet_region return self.get_provider().sociallogin_from_response(request, data) def get_callback_url(self, request, app): r = super().get_callback_url(request, app) region = request.GET.get("region", "").lower() # Pass the region down to the callback URL if we specified it if region and region in self.valid_regions: r += f"?region={region}" return r oauth2_login = OAuth2LoginView.adapter_view(BattleNetOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(BattleNetOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/bitbucket_oauth2/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/bitbucket_oauth2/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.bitbucket_oauth2.views import ( BitbucketOAuth2Adapter, ) from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class BitbucketOAuth2Account(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("links", {}).get("html", {}).get("href") def get_avatar_url(self): return self.account.extra_data.get("links", {}).get("avatar", {}).get("href") class BitbucketOAuth2Provider(OAuth2Provider): id = "bitbucket_oauth2" name = "Bitbucket" account_class = BitbucketOAuth2Account oauth2_adapter_class = BitbucketOAuth2Adapter def extract_uid(self, data): return data["username"] def extract_common_fields(self, data): return dict( email=data.get("email"), username=data.get("username"), name=data.get("display_name"), ) provider_classes = [BitbucketOAuth2Provider] ================================================ FILE: allauth/socialaccount/providers/bitbucket_oauth2/urls.py ================================================ from allauth.socialaccount.providers.bitbucket_oauth2.provider import ( BitbucketOAuth2Provider, ) from allauth.socialaccount.providers.oauth.urls import default_urlpatterns urlpatterns = default_urlpatterns(BitbucketOAuth2Provider) ================================================ FILE: allauth/socialaccount/providers/bitbucket_oauth2/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class BitbucketOAuth2Adapter(OAuth2Adapter): provider_id = "bitbucket_oauth2" access_token_url = "https://bitbucket.org/site/oauth2/access_token" # nosec authorize_url = "https://bitbucket.org/site/oauth2/authorize" profile_url = "https://api.bitbucket.org/2.0/user" emails_url = "https://api.bitbucket.org/2.0/user/emails" def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, params={"access_token": token.token}) extra_data = resp.json() if app_settings.QUERY_EMAIL: if email := self.get_email(token): extra_data["email"] = email return self.get_provider().sociallogin_from_response(request, extra_data) def get_email(self, token) -> str: """Fetches email address from email API endpoint""" with get_adapter().get_requests_session() as sess: resp = sess.get(self.emails_url, params={"access_token": token.token}) emails = resp.json().get("values", []) email = "" try: email = emails[0].get("email") primary_emails = [e for e in emails if e.get("is_primary", False)] email = primary_emails[0].get("email") except (IndexError, TypeError, KeyError): pass return email oauth_login = OAuth2LoginView.adapter_view(BitbucketOAuth2Adapter) oauth_callback = OAuth2CallbackView.adapter_view(BitbucketOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/bitly/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/bitly/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.bitly.views import BitlyOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class BitlyAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("profile_url") def get_avatar_url(self): return self.account.extra_data.get("profile_image") class BitlyProvider(OAuth2Provider): id = "bitly" name = "Bitly" account_class = BitlyAccount oauth2_adapter_class = BitlyOAuth2Adapter def extract_uid(self, data): return str(data["login"]) def extract_common_fields(self, data): return dict(username=data["login"], name=data.get("full_name")) provider_classes = [BitlyProvider] ================================================ FILE: allauth/socialaccount/providers/bitly/urls.py ================================================ from allauth.socialaccount.providers.bitly.provider import BitlyProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(BitlyProvider) ================================================ FILE: allauth/socialaccount/providers/bitly/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class BitlyOAuth2Adapter(OAuth2Adapter): provider_id = "bitly" access_token_url = "https://api-ssl.bitly.com/oauth/access_token" # nosec authorize_url = "https://bitly.com/oauth/authorize" profile_url = "https://api-ssl.bitly.com/v3/user/info" def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, params={"access_token": token.token}) extra_data = resp.json()["data"] return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(BitlyOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(BitlyOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/box/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/box/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.box.views import BoxOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class BoxOAuth2Account(ProviderAccount): pass class BoxOAuth2Provider(OAuth2Provider): id = "box" name = "Box" account_class = BoxOAuth2Account oauth2_adapter_class = BoxOAuth2Adapter def extract_uid(self, data): return data["id"] def extract_common_fields(self, data): return dict(name=data.get("display_name"), email=data.get("email")) provider_classes = [BoxOAuth2Provider] ================================================ FILE: allauth/socialaccount/providers/box/urls.py ================================================ from allauth.socialaccount.providers.box.provider import BoxOAuth2Provider from allauth.socialaccount.providers.oauth.urls import default_urlpatterns urlpatterns = default_urlpatterns(BoxOAuth2Provider) ================================================ FILE: allauth/socialaccount/providers/box/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class BoxOAuth2Adapter(OAuth2Adapter): provider_id = "box" access_token_url = "https://api.box.com/oauth2/token" # nosec authorize_url = "https://account.box.com/api/oauth2/authorize" profile_url = "https://api.box.com/2.0/users/me" redirect_uri_protocol = None def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, params={"access_token": token.token}) # This only here because of weird response from the test suite if isinstance(resp, list): resp = resp[0] extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth_login = OAuth2LoginView.adapter_view(BoxOAuth2Adapter) oauth_callback = OAuth2CallbackView.adapter_view(BoxOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/cilogon/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/cilogon/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount.app_settings import QUERY_EMAIL from allauth.socialaccount.providers.base import AuthAction, ProviderAccount from allauth.socialaccount.providers.cilogon.views import CILogonOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class Scope: OPENID = "openid" EMAIL = "email" PROFILE = "profile" USERINFO = "org.cilogon.userinfo" class CILogonAccount(ProviderAccount): pass class CILogonProvider(OAuth2Provider): id = "cilogon" name = "CILogon" account_class = CILogonAccount oauth2_adapter_class = CILogonOAuth2Adapter def get_default_scope(self): scope = [Scope.PROFILE, Scope.USERINFO, Scope.OPENID] if QUERY_EMAIL: scope.append(Scope.EMAIL) return scope def get_auth_params_from_request(self, request, action): ret = super().get_auth_params_from_request(request, action) if action == AuthAction.REAUTHENTICATE: ret["prompt"] = "select_account consent" return ret def extract_uid(self, data): return str(data.get("sub")) def extract_common_fields(self, data): return dict( email=data.get("email"), last_name=data.get("family_name"), first_name=data.get("given_name"), eppn=data.get("eppn"), ) def extract_email_addresses(self, data): ret = [] email = data.get("email") if email and data.get("verified_email"): ret.append(EmailAddress(email=email, verified=True, primary=True)) return ret provider_classes = [CILogonProvider] ================================================ FILE: allauth/socialaccount/providers/cilogon/urls.py ================================================ from allauth.socialaccount.providers.cilogon.provider import CILogonProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(CILogonProvider) ================================================ FILE: allauth/socialaccount/providers/cilogon/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class CILogonOAuth2Adapter(OAuth2Adapter): provider_id = "cilogon" access_token_url = "https://cilogon.org/oauth2/token" # nosec authorize_url = "https://cilogon.org/authorize" profile_url = "https://cilogon.org/oauth2/userinfo" def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: resp = sess.get( self.profile_url, params={"access_token": token.token, "alt": "json"}, ) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(CILogonOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(CILogonOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/clever/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/clever/provider.py ================================================ from allauth.socialaccount import providers from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.clever.views import CleverOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class CleverAccount(ProviderAccount): def get_avatar_url(self): # return self.account.extra_data.get('user').get('image_192', None) return None def get_user_data(self): return self.account.extra_data.get("data", {}) class CleverProvider(OAuth2Provider): id = "clever" name = "Clever" account_class = CleverAccount oauth2_adapter_class = CleverOAuth2Adapter def extract_uid(self, data): return data["data"]["id"] def get_user_type(self, data): return list(data.get("data", {}).get("roles", {}).keys())[0] def extract_common_fields(self, data): return dict( first_name=data.get("data", {}).get("name", {}).get("first", None), last_name=data.get("data", {}).get("name", {}).get("last", None), username=data.get("data", {}) .get("roles", {}) .get(self.get_user_type(data), {}) .get("credentials", {}) .get("district_username", None), email=data.get("data", {}).get("email", None), ) def get_default_scope(self): return [ "read:district_admins", "read:districts", "read:resources", "read:school_admins", "read:schools", "read:sections", "read:student_contacts", "read:students", "read:teachers", "read:user_id", ] providers.registry.register(CleverProvider) ================================================ FILE: allauth/socialaccount/providers/clever/urls.py ================================================ from allauth.socialaccount.providers.clever.provider import CleverProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(CleverProvider) ================================================ FILE: allauth/socialaccount/providers/clever/views.py ================================================ from http import HTTPStatus from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Error from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class CleverOAuth2Adapter(OAuth2Adapter): provider_id = "clever" access_token_url = "https://clever.com/oauth/tokens" # nosec authorize_url = "https://clever.com/oauth/authorize" identity_url = "https://api.clever.com/v3.0/me" user_details_url = "https://api.clever.com/v3.0/users" def complete_login(self, request, app, token, **kwargs): extra_data = self.get_data(token.token) return self.get_provider().sociallogin_from_response(request, extra_data) def get_data(self, token): headers = {"Authorization": f"Bearer {token}"} with get_adapter().get_requests_session() as sess: # Verify the user first resp = sess.get(self.identity_url, headers=headers) if resp.status_code != HTTPStatus.OK: raise OAuth2Error() resp = resp.json() user_id = resp["data"]["id"] details_url = f"{self.user_details_url}/{user_id}" resp = sess.get(details_url, headers=headers) resp.raise_for_status() return resp.json() oauth2_login = OAuth2LoginView.adapter_view(CleverOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(CleverOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/coinbase/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/coinbase/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.coinbase.views import CoinbaseOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class CoinbaseAccount(ProviderAccount): def get_avatar_url(self): return None class CoinbaseProvider(OAuth2Provider): id = "coinbase" name = "Coinbase" account_class = CoinbaseAccount oauth2_adapter_class = CoinbaseOAuth2Adapter def get_default_scope(self): # See: https://coinbase.com/docs/api/permissions return ["wallet:user:email"] def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): # See: https://coinbase.com/api/doc/1.0/users/index.html return dict(email=data["email"]) provider_classes = [CoinbaseProvider] ================================================ FILE: allauth/socialaccount/providers/coinbase/urls.py ================================================ from allauth.socialaccount.providers.coinbase.provider import CoinbaseProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(CoinbaseProvider) ================================================ FILE: allauth/socialaccount/providers/coinbase/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class CoinbaseOAuth2Adapter(OAuth2Adapter): provider_id = "coinbase" @property def authorize_url(self): return "https://www.coinbase.com/oauth/authorize" @property def access_token_url(self): return "https://www.coinbase.com/oauth/token" @property def profile_url(self): return "https://api.coinbase.com/v2/user" def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: response = sess.get(self.profile_url, params={"access_token": token}) extra_data = response.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(CoinbaseOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(CoinbaseOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/dataporten/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/dataporten/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/dataporten/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.dataporten.views import DataportenOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class DataportenAccount(ProviderAccount): def get_avatar_url(self): """ Returns a valid URL to an 128x128 .png photo of the user """ # Documentation for user profile photos can be found here: # https://docs.dataporten.no/docs/oauth-authentication/ photo = self.account.extra_data["profilephoto"] return f"https://api.dataporten.no/userinfo/v1/user/media/{photo}" class DataportenProvider(OAuth2Provider): id = "dataporten" name = "Dataporten" account_class = DataportenAccount oauth2_adapter_class = DataportenOAuth2Adapter def extract_uid(self, data): """ Returns the primary user identifier, an UUID string See: https://docs.dataporten.no/docs/userid/ """ return data["userid"] def extract_extra_data(self, data): """ Extracts fields from `data` that will be stored in `SocialAccount`'s `extra_data` JSONField. All the necessary data extraction has already been done in the complete_login()-view, so we can just return the data. PS: This is default behaviour, so we did not really need to define this function, but it is included for documentation purposes. Typical return dict: { "userid": "76a7a061-3c55-430d-8ee0-6f82ec42501f", "userid_sec": ["feide:andreas@uninett.no"], "name": "Andreas \u00c5kre Solberg", "email": "andreas.solberg@uninett.no", "profilephoto": "p:a3019954-902f-45a3-b4ee-bca7b48ab507", } """ return data def extract_common_fields(self, data): """ This function extracts information from the /userinfo endpoint which will be consumed by allauth.socialaccount.adapter.populate_user(). Look there to find which key-value pairs that should be saved in the returned dict. Typical return dict: { "userid": "76a7a061-3c55-430d-8ee0-6f82ec42501f", "userid_sec": ["feide:andreas@uninett.no"], "name": "Andreas \u00c5kre Solberg", "email": "andreas.solberg@uninett.no", "profilephoto": "p:a3019954-902f-45a3-b4ee-bca7b48ab507", "username": "andreas", } """ # Make shallow copy to prevent possible mutability issues data = dict(data) # If a Feide username is available, use it. If not, use the "username" # of the email-address for userid in data.get("userid_sec"): usertype, username = userid.split(":") if usertype == "feide": data["username"] = username.split("@")[0] break else: # Only entered if break is not executed above data["username"] = data.get("email").split("@")[0] return data provider_classes = [DataportenProvider] ================================================ FILE: allauth/socialaccount/providers/dataporten/urls.py ================================================ from allauth.socialaccount.providers.dataporten.provider import DataportenProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(DataportenProvider) ================================================ FILE: allauth/socialaccount/providers/dataporten/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.base import ProviderException from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class DataportenOAuth2Adapter(OAuth2Adapter): provider_id = "dataporten" access_token_url = "https://auth.dataporten.no/oauth/token" # nosec authorize_url = "https://auth.dataporten.no/oauth/authorization" profile_url = "https://auth.dataporten.no/userinfo" groups_url = "https://groups-api.dataporten.no/groups/" def complete_login(self, request, app, token, **kwargs): """ Arguments: request - The get request to the callback URL /accounts/dataporten/login/callback. app - The corresponding SocialApp model instance token - A token object with access token given in token.token Returns: Should return a dict with user information intended for parsing by the methods of the DataportenProvider view, i.e. extract_uid(), extract_extra_data(), and extract_common_fields() """ # The authentication header headers = {"Authorization": f"Bearer {token.token}"} # Userinfo endpoint, for documentation see: # https://docs.dataporten.no/docs/oauth-authentication/ with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) resp.raise_for_status() response_json = resp.json() extra_data = response_json["user"] # Finally test that the audience property matches the client id # for validification reasons, as instructed by the Dataporten docs # if the userinfo-response is used for authentication if response_json["audience"] != app.client_id: raise ProviderException( "Dataporten returned a user with an audience field \ which does not correspond to the client id of the \ application." ) return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(DataportenOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(DataportenOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/daum/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/daum/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/daum/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.daum.views import DaumOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class DaumAccount(ProviderAccount): def get_avatar_url(self): return self.account.extra_data.get("bigImagePath") class DaumProvider(OAuth2Provider): id = "Daum" name = "Daum" account_class = DaumAccount oauth2_adapter_class = DaumOAuth2Adapter def extract_uid(self, data): return str(data.get("id")) provider_classes = [DaumProvider] ================================================ FILE: allauth/socialaccount/providers/daum/urls.py ================================================ from allauth.socialaccount.providers.daum.provider import DaumProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(DaumProvider) ================================================ FILE: allauth/socialaccount/providers/daum/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class DaumOAuth2Adapter(OAuth2Adapter): provider_id = "Daum" access_token_url = "https://apis.daum.net/oauth2/token" # nosec authorize_url = "https://apis.daum.net/oauth2/authorize" profile_url = "https://apis.daum.net/user/v1/show.json" def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, params={"access_token": token.token}) extra_data = resp.json().get("result") return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(DaumOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(DaumOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/digitalocean/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/digitalocean/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.digitalocean.views import DigitalOceanOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class DigitalOceanAccount(ProviderAccount): def get_user_data(self): return self.account.extra_data.get("account", {}) class DigitalOceanProvider(OAuth2Provider): id = "digitalocean" name = "DigitalOcean" account_class = DigitalOceanAccount oauth2_adapter_class = DigitalOceanOAuth2Adapter def extract_uid(self, data): return str(data["account"]["uuid"]) def extract_common_fields(self, data): return dict(email=data["account"]["email"]) provider_classes = [DigitalOceanProvider] ================================================ FILE: allauth/socialaccount/providers/digitalocean/urls.py ================================================ from allauth.socialaccount.providers.digitalocean.provider import DigitalOceanProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(DigitalOceanProvider) ================================================ FILE: allauth/socialaccount/providers/digitalocean/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class DigitalOceanOAuth2Adapter(OAuth2Adapter): provider_id = "digitalocean" access_token_url = "https://cloud.digitalocean.com/v1/oauth/token" # nosec authorize_url = "https://cloud.digitalocean.com/v1/oauth/authorize" profile_url = "https://api.digitalocean.com/v2/account" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(DigitalOceanOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(DigitalOceanOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/dingtalk/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/dingtalk/client.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Client, OAuth2Error class DingTalkOAuth2Client(OAuth2Client): def get_access_token(self, code, pkce_code_verifier=None): data = { "clientId": self.consumer_key, "clientSecret": self.consumer_secret, "code": code, "grantType": "authorization_code", } params = None if pkce_code_verifier: data["code_verifier"] = pkce_code_verifier self._strip_empty_keys(data) url = self.access_token_url if self.access_token_method == "GET": # nosec params = data data = None with get_adapter().get_requests_session() as sess: resp = sess.request(self.access_token_method, url, params=params, json=data) resp.raise_for_status() access_token = resp.json() if not access_token or "accessToken" not in access_token: raise OAuth2Error(f"Error retrieving access token: {resp.content}") access_token["access_token"] = access_token.pop("accessToken") access_token["refresh_token"] = access_token.pop("refreshToken") access_token["expires_in"] = access_token.pop("expireIn") return access_token ================================================ FILE: allauth/socialaccount/providers/dingtalk/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.dingtalk.views import DingTalkOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class DingTalkAccount(ProviderAccount): def get_avatar_url(self): return self.account.extra_data.get("avatarUrl") def to_str(self): return self.account.extra_data.get("nick", super().to_str()) class DingTalkProvider(OAuth2Provider): id = "dingtalk" name = "DingTalk" account_class = DingTalkAccount oauth2_adapter_class = DingTalkOAuth2Adapter def extract_uid(self, data): return data["openId"] def get_default_scope(self): return ["openid", "corpid"] def extract_common_fields(self, data): return dict(username=data.get("nick"), name=data.get("nick")) provider_classes = [DingTalkProvider] ================================================ FILE: allauth/socialaccount/providers/dingtalk/urls.py ================================================ from allauth.socialaccount.providers.dingtalk.provider import DingTalkProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(DingTalkProvider) ================================================ FILE: allauth/socialaccount/providers/dingtalk/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) from .client import DingTalkOAuth2Client class DingTalkOAuth2Adapter(OAuth2Adapter): provider_id = "dingtalk" access_token_url = "https://api.dingtalk.com/v1.0/oauth2/userAccessToken" # nosec authorize_url = "https://login.dingtalk.com/oauth2/auth" profile_url = "https://api.dingtalk.com/v1.0/contact/users/me" client_class = DingTalkOAuth2Client def __init__(self, request): # dingtalk set "authCode" instead of "code" in callback url if "authCode" in request.GET: request.GET._mutable = True request.GET["code"] = request.GET["authCode"] request.GET._mutable = False super().__init__(request) def complete_login(self, request, app, token, **kwargs): headers = {"x-acs-dingtalk-access-token": token.token} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(DingTalkOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(DingTalkOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/discogs/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/discogs/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.discogs.views import DiscogsOAuthAdapter from allauth.socialaccount.providers.oauth.provider import OAuthProvider class DiscogsAccount(ProviderAccount): def get_username(self): return self.account.extra_data.get("username") def get_profile_url(self): return self.account.extra_data.get("resource_url") class DiscogsProvider(OAuthProvider): id = "discogs" name = "discogs" account_class = DiscogsAccount oauth_adapter_class = DiscogsOAuthAdapter def extract_uid(self, data): return str(data["id"]) provider_classes = [DiscogsProvider] ================================================ FILE: allauth/socialaccount/providers/discogs/urls.py ================================================ from allauth.socialaccount.providers.discogs.provider import DiscogsProvider from allauth.socialaccount.providers.oauth.urls import default_urlpatterns urlpatterns = default_urlpatterns(DiscogsProvider) ================================================ FILE: allauth/socialaccount/providers/discogs/views.py ================================================ from allauth.socialaccount.providers.oauth.client import OAuth from allauth.socialaccount.providers.oauth.views import ( OAuthAdapter, OAuthCallbackView, OAuthLoginView, ) class DiscogsAPI(OAuth): url = "https://api.discogs.com/oauth/identity" def get_user_info(self): data = self.query(self.url).json() return data class DiscogsOAuthAdapter(OAuthAdapter): provider_id = "discogs" request_token_url = "https://api.discogs.com/oauth/request_token" # nosec access_token_url = "https://api.discogs.com/oauth/access_token" # nosec authorize_url = "https://discogs.com/oauth/authorize" def complete_login(self, request, app, token, response): client = DiscogsAPI(request, app.client_id, app.secret, self.request_token_url) extra_data = client.get_user_info() return self.get_provider().sociallogin_from_response(request, extra_data) oauth_login = OAuthLoginView.adapter_view(DiscogsOAuthAdapter) oauth_callback = OAuthCallbackView.adapter_view(DiscogsOAuthAdapter) ================================================ FILE: allauth/socialaccount/providers/discord/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/discord/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base import AuthAction, ProviderAccount from allauth.socialaccount.providers.discord.views import DiscordOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class DiscordAccount(ProviderAccount): def validate_descriminator(self, discriminator): if not isinstance(discriminator, str): return False # As of 2023-06-22, Discord returns string literal '0' for users # with no discriminator return len(discriminator) == 4 if discriminator.isdigit() else False def is_new_username_system(self): if not isinstance(self.account.extra_data, dict): return None discriminator = self.account.extra_data.get("discriminator") if self.validate_descriminator(discriminator): return False if self.account.extra_data.get("global_name") is not None: return True return None def to_str(self): fallback = super().to_str() # If the extra_data is malformed, exit early if not isinstance(self.account.extra_data, dict): return fallback is_new_system = self.is_new_username_system() if is_new_system is None: # We couldn't determine if the username is new or old # so we'll just return the username on it's own. display_name = self.account.extra_data.get("username") elif is_new_system: # global_name can be None or even undefined # so we'll use the username as a fallback global_name = self.account.extra_data.get("global_name") username = self.account.extra_data.get("username") display_name = global_name or username else: # Looks like it's the old username system # so we'll just use the username and discriminator display_name = "{username}#{discriminator}".format( username=self.account.extra_data.get("username"), discriminator=self.account.extra_data.get("discriminator"), ) # It's very unlikely but still possible that the display_name is None # so we'll return or'd against the fallback just incase. We don't want # to return None as users of the library expect this to be str. return display_name or fallback def get_avatar_url(self): if ( "id" in self.account.extra_data.keys() and "avatar" in self.account.extra_data.keys() ): return "https://cdn.discordapp.com/avatars/{id}/{avatar}.png".format( **self.account.extra_data ) class DiscordProvider(OAuth2Provider): id = "discord" name = "Discord" account_class = DiscordAccount oauth2_adapter_class = DiscordOAuth2Adapter def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict( email=data.get("email"), username=data.get("username"), name=data.get("username"), ) def get_auth_params_from_request(self, request, action): ret = super().get_auth_params_from_request(request, action) if action == AuthAction.REAUTHENTICATE: ret["prompt"] = "consent" return ret def get_default_scope(self): return ["email", "identify"] def extract_email_addresses(self, data): ret = [] email = data.get("email") if email and data.get("verified"): ret.append(EmailAddress(email=email, verified=True, primary=True)) return ret provider_classes = [DiscordProvider] ================================================ FILE: allauth/socialaccount/providers/discord/urls.py ================================================ from allauth.socialaccount.providers.discord.provider import DiscordProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(DiscordProvider) ================================================ FILE: allauth/socialaccount/providers/discord/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class DiscordOAuth2Adapter(OAuth2Adapter): provider_id = "discord" access_token_url = "https://discord.com/api/oauth2/token" # nosec authorize_url = "https://discord.com/api/oauth2/authorize" profile_url = "https://discord.com/api/users/@me" def complete_login(self, request, app, token, **kwargs): headers = { "Authorization": f"Bearer {token.token}", "Content-Type": "application/json", } with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(DiscordOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(DiscordOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/disqus/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/disqus/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount.app_settings import QUERY_EMAIL from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.disqus.views import DisqusOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class DisqusAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("profileUrl") def get_avatar_url(self): return self.account.extra_data.get("avatar", {}).get("permalink") class DisqusProvider(OAuth2Provider): id = "disqus" name = "Disqus" account_class = DisqusAccount oauth2_adapter_class = DisqusOAuth2Adapter def get_default_scope(self): scope = ["read"] if QUERY_EMAIL: scope += ["email"] return scope def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return { "username": data.get("username"), "email": data.get("email"), "name": data.get("name"), } def extract_email_addresses(self, data): ret = [] email = data.get("email") if email: ret.append(EmailAddress(email=email, verified=True, primary=True)) return ret provider_classes = [DisqusProvider] ================================================ FILE: allauth/socialaccount/providers/disqus/urls.py ================================================ from allauth.socialaccount.providers.disqus.provider import DisqusProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(DisqusProvider) ================================================ FILE: allauth/socialaccount/providers/disqus/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class DisqusOAuth2Adapter(OAuth2Adapter): provider_id = "disqus" access_token_url = "https://disqus.com/api/oauth/2.0/access_token/" # nosec authorize_url = "https://disqus.com/api/oauth/2.0/authorize/" profile_url = "https://disqus.com/api/3.0/users/details.json" scope_delimiter = "," def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: params = { "access_token": token.token, "api_key": app.client_id, "api_secret": app.secret, } resp = sess.get(self.profile_url, params=params) resp.raise_for_status() extra_data = resp.json().get("response") login = self.get_provider().sociallogin_from_response(request, extra_data) return login oauth2_login = OAuth2LoginView.adapter_view(DisqusOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(DisqusOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/douban/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/douban/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.douban.views import DoubanOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class DoubanAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("alt") def get_avatar_url(self): return self.account.extra_data.get("large_avatar") class DoubanProvider(OAuth2Provider): id = "douban" name = "Douban" account_class = DoubanAccount oauth2_adapter_class = DoubanOAuth2Adapter def extract_uid(self, data): return data["id"] def extract_common_fields(self, data): """ Extract data from profile json to populate user instance. In Douban profile API: - id: a digital string, will never change - uid: defaults to id, but can be changed once, used in profile url, like slug - name: display name, can be changed every 30 days So we should use `id` as username here, other than `uid`. Also use `name` as `first_name` for displaying purpose. """ return { "username": data["id"], "first_name": data.get("name", ""), } provider_classes = [DoubanProvider] ================================================ FILE: allauth/socialaccount/providers/douban/urls.py ================================================ from allauth.socialaccount.providers.douban.provider import DoubanProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(DoubanProvider) ================================================ FILE: allauth/socialaccount/providers/douban/views.py ================================================ from django.utils.translation import gettext_lazy as _ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) from ..base import ProviderException class DoubanOAuth2Adapter(OAuth2Adapter): provider_id = "douban" access_token_url = "https://www.douban.com/service/auth2/token" # nosec authorize_url = "https://www.douban.com/service/auth2/auth" profile_url = "https://api.douban.com/v2/user/~me" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) extra_data = resp.json() """ Douban may return data like this: { 'code': 128, 'request': 'GET /v2/user/~me', 'msg': 'user_is_locked:53358092' } """ if "id" not in extra_data: msg = extra_data.get("msg", _("Invalid profile data")) raise ProviderException(msg) return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(DoubanOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(DoubanOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/doximity/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/doximity/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.doximity.views import DoximityOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class DoximityAccount(ProviderAccount): def get_profile_url(self): return None def get_avatar_url(self): return self.account.extra_data.get("profile_photo") class DoximityProvider(OAuth2Provider): id = "doximity" name = "Doximity" account_class = DoximityAccount oauth2_adapter_class = DoximityOAuth2Adapter def extract_uid(self, data): return str(data["id"]) # the Doximity id is long def extract_common_fields(self, data): return dict( username=data.get("email"), first_name=data.get("firstname"), last_name=data.get("lastname"), email=data.get("email"), name=data.get("full_name"), ) def get_default_scope(self): return ["basic", "email"] provider_classes = [DoximityProvider] ================================================ FILE: allauth/socialaccount/providers/doximity/urls.py ================================================ from allauth.socialaccount.providers.doximity.provider import DoximityProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(DoximityProvider) ================================================ FILE: allauth/socialaccount/providers/doximity/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class DoximityOAuth2Adapter(OAuth2Adapter): provider_id = "doximity" access_token_url = "https://auth.doximity.com/oauth/token" # nosec authorize_url = "https://auth.doximity.com/oauth/authorize" profile_url = "https://www.doximity.com/api/v1/users/current" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(DoximityOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(DoximityOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/draugiem/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/draugiem/provider.py ================================================ from django.urls import reverse from django.utils.http import urlencode from allauth.socialaccount.providers.base import Provider, ProviderAccount class DraugiemAccount(ProviderAccount): def get_avatar_url(self): ret = None pic_small_url = self.account.extra_data.get("img") pic_icon_url = self.account.extra_data.get("imgi") pic_medium_url = self.account.extra_data.get("imgm") pic_large_url = self.account.extra_data.get("imgl") if pic_large_url: return pic_large_url elif pic_medium_url: return pic_medium_url elif pic_icon_url: return pic_icon_url elif pic_small_url: return pic_small_url else: return ret class DraugiemProvider(Provider): id = "draugiem" name = "Draugiem" account_class = DraugiemAccount def get_login_url(self, request, **kwargs): url = reverse(f"{self.id}_login") if kwargs: url = f"{url}?{urlencode(kwargs)}" return url def extract_uid(self, data): return str(data["uid"]) def extract_common_fields(self, data): uid = self.extract_uid(data) user_data = data["users"][uid] return dict( first_name=user_data.get("name"), last_name=user_data.get("surname"), ) def extract_extra_data(self, data): uid = self.extract_uid(data) return data["users"][uid] provider_classes = [DraugiemProvider] ================================================ FILE: allauth/socialaccount/providers/draugiem/urls.py ================================================ from django.urls import path from . import views urlpatterns = [ path("draugiem/login/", views.login, name="draugiem_login"), path("draugiem/callback/", views.callback, name="draugiem_callback"), ] ================================================ FILE: allauth/socialaccount/providers/draugiem/views.py ================================================ import requests from hashlib import md5 from django.http import HttpResponseRedirect from django.urls import reverse from django.utils.http import urlencode from django.views.decorators.csrf import csrf_exempt from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.helpers import ( complete_social_login, render_authentication_error, ) from allauth.socialaccount.models import SocialLogin, SocialToken from allauth.socialaccount.providers.draugiem.provider import DraugiemProvider from ..base import AuthError class DraugiemApiError(Exception): pass ACCESS_TOKEN_URL = "https://api.draugiem.lv/json" # nosec AUTHORIZE_URL = "https://api.draugiem.lv/authorize" def login(request): app = get_adapter().get_app(request, DraugiemProvider.id) redirect_url = request.build_absolute_uri(reverse(callback)) # Draugiem mandates a weak hashing algorithm. redirect_url_hash = md5(f"{app.secret}{redirect_url}".encode()).hexdigest() # nosec params = { "app": app.client_id, "hash": redirect_url_hash, "redirect": redirect_url, } SocialLogin.stash_state(request) return HttpResponseRedirect(f"{AUTHORIZE_URL}?{urlencode(params)}") @csrf_exempt def callback(request): adapter = get_adapter() provider = adapter.get_provider(request, DraugiemProvider.id) if "dr_auth_status" not in request.GET: return render_authentication_error(request, provider, error=AuthError.UNKNOWN) if request.GET["dr_auth_status"] != "ok": return render_authentication_error(request, provider, error=AuthError.DENIED) if "dr_auth_code" not in request.GET: return render_authentication_error(request, provider, error=AuthError.UNKNOWN) ret = None auth_exception = None try: app = provider.app login = draugiem_complete_login(request, app, request.GET["dr_auth_code"]) login.state = SocialLogin.unstash_state(request) ret = complete_social_login(request, login) except (requests.RequestException, DraugiemApiError) as e: auth_exception = e if not ret: ret = render_authentication_error(request, provider, exception=auth_exception) return ret def draugiem_complete_login(request, app, code): provider = get_adapter().get_provider(request, DraugiemProvider.id) with get_adapter().get_requests_session() as sess: params = {"action": "authorize", "app": app.secret, "code": code} response = sess.get(ACCESS_TOKEN_URL, params=params) response.raise_for_status() response_json = response.json() if "error" in response_json: raise DraugiemApiError(response_json["error"]) token = SocialToken(app=app, token=response_json["apikey"]) login = provider.sociallogin_from_response(request, response_json) login.token = token return login ================================================ FILE: allauth/socialaccount/providers/drip/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/drip/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.drip.views import DripOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class DripAccount(ProviderAccount): pass class DripProvider(OAuth2Provider): id = "drip" name = "Drip" account_class = DripAccount oauth2_adapter_class = DripOAuth2Adapter def extract_uid(self, data): # no uid available, we generate one by hashing the email uid = hash(data.get("email")) return str(uid) def extract_common_fields(self, data): return dict(email=data.get("email"), name=data.get("name")) def extract_email_addresses(self, data): ret = [] email = data.get("email") if email: ret.append(EmailAddress(email=email, verified=True, primary=True)) return ret provider_classes = [DripProvider] ================================================ FILE: allauth/socialaccount/providers/drip/urls.py ================================================ from allauth.socialaccount.providers.drip.provider import DripProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(DripProvider) ================================================ FILE: allauth/socialaccount/providers/drip/views.py ================================================ """Views for Drip API.""" from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class DripOAuth2Adapter(OAuth2Adapter): """OAuth2Adapter for Drip API v3.""" provider_id = "drip" authorize_url = "https://www.getdrip.com/oauth/authorize" access_token_url = "https://www.getdrip.com/oauth/token" # nosec profile_url = "https://api.getdrip.com/v2/user" def complete_login(self, request, app, token, **kwargs): """Complete login, ensuring correct OAuth header.""" headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: response = sess.get(self.profile_url, headers=headers) response.raise_for_status() extra_data = response.json()["users"][0] return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(DripOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(DripOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/dropbox/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/dropbox/provider.py ================================================ from allauth.socialaccount import providers from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.dropbox.views import DropboxOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class DropboxOAuth2Account(ProviderAccount): pass class DropboxOAuth2Provider(OAuth2Provider): id = "dropbox" name = "Dropbox" account_class = DropboxOAuth2Account oauth2_adapter_class = DropboxOAuth2Adapter def extract_uid(self, data): return data["account_id"] def extract_common_fields(self, data): return dict(name=data["name"]["display_name"], email=data["email"]) providers.registry.register(DropboxOAuth2Provider) ================================================ FILE: allauth/socialaccount/providers/dropbox/urls.py ================================================ from allauth.socialaccount.providers.dropbox.provider import DropboxOAuth2Provider from allauth.socialaccount.providers.oauth.urls import default_urlpatterns urlpatterns = default_urlpatterns(DropboxOAuth2Provider) ================================================ FILE: allauth/socialaccount/providers/dropbox/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class DropboxOAuth2Adapter(OAuth2Adapter): provider_id = "dropbox" access_token_url = "https://api.dropbox.com/oauth2/token" # nosec authorize_url = "https://www.dropbox.com/oauth2/authorize" profile_url = "https://api.dropbox.com/2/users/get_current_account" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: response = sess.post(self.profile_url, headers=headers) response.raise_for_status() extra_data = response.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth_login = OAuth2LoginView.adapter_view(DropboxOAuth2Adapter) oauth_callback = OAuth2CallbackView.adapter_view(DropboxOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/dummy/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/dummy/forms.py ================================================ from django import forms from allauth.account.fields import EmailField class AuthenticateForm(forms.Form): id = forms.IntegerField(label="Account ID") email = EmailField(required=False) email_verified = forms.BooleanField(required=False) username = forms.CharField(required=False) first_name = forms.CharField(required=False) last_name = forms.CharField(required=False) phone = forms.CharField(required=False) phone_verified = forms.BooleanField(required=False) ================================================ FILE: allauth/socialaccount/providers/dummy/provider.py ================================================ import json from django.http import HttpResponseRedirect from django.urls import reverse from django.utils.http import urlencode from allauth.account.models import EmailAddress from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.base import Provider, ProviderAccount from allauth.socialaccount.providers.dummy.forms import AuthenticateForm class DummyAccount(ProviderAccount): pass class DummyProvider(Provider): id = "dummy" name = "Dummy" account_class = DummyAccount uses_apps = False supports_redirect = True supports_token_authentication = True def get_login_url(self, request, **kwargs): url = reverse("dummy_login") if kwargs: url = f"{url}?{urlencode(kwargs)}" return url def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): ret = {} if first_name := data.get("first_name"): ret["first_name"] = first_name if last_name := data.get("last_name"): ret["last_name"] = last_name if username := data.get("username"): ret["username"] = username if phone := data.get("phone"): ret["phone"] = phone if phone_verified := data.get("phone_verified"): ret["phone_verified"] = phone_verified return ret def redirect(self, request, process, next_url=None, data=None, **kwargs): state_id = self.stash_redirect_state( request, process, next_url=next_url, data=data, **kwargs, ) return HttpResponseRedirect( f"{reverse('dummy_authenticate')}?{urlencode({'state': state_id})}" ) def extract_email_addresses(self, data): addresses = [] email = data.get("email") if email: email_verified = data.get("email_verified") addresses.append( EmailAddress( email=email, verified=email_verified, primary=True, ) ) return addresses def verify_token(self, request, token): # Our ID token is just a JSON payload that can be handed over # to the `AuthenticateForm`. id_token = token.get("id_token") if id_token: try: data = json.loads(id_token) except json.JSONDecodeError: pass else: form = AuthenticateForm(data=data) if form.is_valid(): return self.sociallogin_from_response(request, form.cleaned_data) raise get_adapter().validation_error("invalid_token") provider_classes = [DummyProvider] ================================================ FILE: allauth/socialaccount/providers/dummy/templates/dummy/authenticate_form.html ================================================ {% extends "socialaccount/base_entrance.html" %} {% load i18n %} {% load allauth %} {% block content %} {% element h1 %} Dummy Provider Login {% endelement %} {% element form form=form method="post" action=action_url %} {% slot body %} {% csrf_token %} {% element fields form=form %} {% endelement %} {% endslot %} {% slot actions %} {% element button type="submit" %} {% trans "Login" %} {% endelement %} {% element button form="cancel-form" type="submit" %} {% trans "Cancel" %} {% endelement %} {% endslot %} {% endelement %}
{% csrf_token %}
{% endblock %} ================================================ FILE: allauth/socialaccount/providers/dummy/urls.py ================================================ from django.urls import path from . import views urlpatterns = [ path("dummy/login/", views.login, name="dummy_login"), path("dummy/authenticate/", views.authenticate, name="dummy_authenticate"), ] ================================================ FILE: allauth/socialaccount/providers/dummy/views.py ================================================ from django.core.exceptions import PermissionDenied from django.http import HttpResponse, HttpResponseBase from django.urls import reverse from django.utils.decorators import method_decorator from django.utils.http import urlencode from django.views.generic.edit import FormView from allauth.account.internal.decorators import login_not_required from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.helpers import ( complete_social_login, render_authentication_error, ) from allauth.socialaccount.models import SocialLogin from allauth.socialaccount.providers.base.constants import AuthError from allauth.socialaccount.providers.base.views import BaseLoginView from allauth.socialaccount.providers.dummy.forms import AuthenticateForm from allauth.socialaccount.providers.dummy.provider import DummyProvider @method_decorator(login_not_required, name="dispatch") class LoginView(BaseLoginView): provider_id = DummyProvider.id login = LoginView.as_view() class AuthenticateView(FormView): form_class = AuthenticateForm template_name = "dummy/authenticate_form.html" @method_decorator(login_not_required) def dispatch(self, request, *args, **kwargs) -> HttpResponseBase: self.state_id = request.GET.get("state") if not self.state_id: raise PermissionDenied() self.provider = get_adapter().get_provider(self.request, DummyProvider.id) if request.method == "POST" and request.POST.get("action") == "cancel": return render_authentication_error( request, self.provider, error=AuthError.CANCELLED, extra_context={"state_id": self.state_id}, ) return super().dispatch(request, *args, **kwargs) def form_valid(self, form) -> HttpResponse: login = self.provider.sociallogin_from_response(self.request, form.cleaned_data) login.state = SocialLogin.unstash_state(self.request) return complete_social_login(self.request, login) def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) ret["action_url"] = ( f"{reverse('dummy_authenticate')}?{urlencode({'state': self.state_id})}" ) return ret authenticate = AuthenticateView.as_view() ================================================ FILE: allauth/socialaccount/providers/dwolla/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/dwolla/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/dwolla/provider.py ================================================ """Provider for Dwolla""" from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.dwolla.views import DwollaOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class DwollaAccount(ProviderAccount): pass class DwollaProvider(OAuth2Provider): """Provider for Dwolla""" id = "dwolla" name = "Dwolla" account_class = DwollaAccount oauth2_adapter_class = DwollaOAuth2Adapter def extract_uid(self, data): return str(data.get("id", None)) def extract_common_fields(self, data): return dict( name=data.get("name"), ) provider_classes = [DwollaProvider] ================================================ FILE: allauth/socialaccount/providers/dwolla/urls.py ================================================ from allauth.socialaccount.providers.dwolla.provider import DwollaProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(DwollaProvider) ================================================ FILE: allauth/socialaccount/providers/dwolla/views.py ================================================ from django.conf import settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) ENVIRONMENTS = { "production": { "auth_url": "https://www.dwolla.com/oauth/v2/authenticate", "token_url": "https://www.dwolla.com/oauth/v2/token", # nosec }, "sandbox": { "auth_url": "https://uat.dwolla.com/oauth/v2/authenticate", "token_url": "https://uat.dwolla.com/oauth/v2/token", # nosec }, } ENV = ( getattr(settings, "SOCIALACCOUNT_PROVIDERS", {}) .get("dwolla", {}) .get("ENVIRONMENT", "production") ) AUTH_URL = ENVIRONMENTS[ENV]["auth_url"] TOKEN_URL = ENVIRONMENTS[ENV]["token_url"] class DwollaOAuth2Adapter(OAuth2Adapter): """Dwolla Views Adapter""" scope_delimiter = "|" provider_id = "dwolla" access_token_url = TOKEN_URL authorize_url = AUTH_URL def complete_login(self, request, app, token, response, **kwargs): with get_adapter().get_requests_session() as sess: resp = sess.get( response["_links"]["account"]["href"], headers={ "authorization": f"Bearer {token.token}", "accept": "application/vnd.dwolla.v1.hal+json", }, ) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(DwollaOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(DwollaOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/edmodo/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/edmodo/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.edmodo.views import EdmodoOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class EdmodoAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("profile_url") def get_avatar_url(self): return self.account.extra_data.get("avatar_url") class EdmodoProvider(OAuth2Provider): id = "edmodo" name = "Edmodo" account_class = EdmodoAccount oauth2_adapter_class = EdmodoOAuth2Adapter def get_default_scope(self): return ["basic"] def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict( first_name=data.get("first_name"), last_name=data.get("last_name"), email=data.get("email", ""), ) def extract_extra_data(self, data): ret = dict(data) # NOTE: For backwards compatibility ret["user_type"] = data.get("type") ret["profile_url"] = data.get("url") ret["avatar_url"] = data.get("avatars", {}).get("large") # (end NOTE) return ret provider_classes = [EdmodoProvider] ================================================ FILE: allauth/socialaccount/providers/edmodo/urls.py ================================================ from allauth.socialaccount.providers.edmodo.provider import EdmodoProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(EdmodoProvider) ================================================ FILE: allauth/socialaccount/providers/edmodo/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class EdmodoOAuth2Adapter(OAuth2Adapter): provider_id = "edmodo" access_token_url = "https://api.edmodo.com/oauth/token" # nosec authorize_url = "https://api.edmodo.com/oauth/authorize" profile_url = "https://api.edmodo.com/users/me" def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, params={"access_token": token.token}) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(EdmodoOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(EdmodoOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/edx/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/edx/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.edx.views import EdxOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class EdxAccount(ProviderAccount): def get_profile_url(self): if self.account.extra_data["profile_image"]["has_image"]: return self.account.extra_data["image_url_full"] class EdxProvider(OAuth2Provider): id = "edx" name = "Edx" account_class = EdxAccount oauth2_adapter_class = EdxOAuth2Adapter def get_default_scope(self): return ["profile"] def extract_uid(self, data): """Extract uid ('id') and ensure it's a str.""" return str(data["username"]) def extract_common_fields(self, data): return dict( email=data.get("email"), username=data.get("username"), name=data.get("name"), user_id=data.get("user_id"), ) provider_classes = [EdxProvider] ================================================ FILE: allauth/socialaccount/providers/edx/urls.py ================================================ from allauth.socialaccount.providers.edx.provider import EdxProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(EdxProvider) ================================================ FILE: allauth/socialaccount/providers/edx/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class EdxOAuth2Adapter(OAuth2Adapter): provider_id = "edx" provider_default_url = "https://edx.org" settings = app_settings.PROVIDERS.get(provider_id, {}) provider_base_url = settings.get("EDX_URL", provider_default_url) access_token_url = f"{provider_base_url}/oauth2/access_token" authorize_url = f"{provider_base_url}/oauth2/authorize/" profile_url = f"{provider_base_url}/api/user/v1/me" account_url = "{0}/api/user/v1/accounts/{1}" supports_state = False redirect_uri_protocol = "https" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: response = sess.get(self.profile_url, headers=headers) extra_data = response.json() if extra_data.get("email", None) is None: account_url = self.account_url.format( self.provider_base_url, extra_data["username"] ) response = sess.get(account_url, headers=headers) extra_data = response.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(EdxOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(EdxOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/eventbrite/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/eventbrite/provider.py ================================================ """Customise Provider classes for Eventbrite API v3.""" from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.eventbrite.views import EventbriteOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class EventbriteAccount(ProviderAccount): """ProviderAccount subclass for Eventbrite.""" def get_avatar_url(self): """Return avatar url.""" return self.account.extra_data["image_id"] def to_str(self): emails = self.account.extra_data.get("emails") if emails: return emails[0]["email"] return super().to_str() class EventbriteProvider(OAuth2Provider): """OAuth2Provider subclass for Eventbrite.""" id = "eventbrite" name = "Eventbrite" account_class = EventbriteAccount oauth2_adapter_class = EventbriteOAuth2Adapter def extract_uid(self, data): """Extract uid ('id') and ensure it's a str.""" return str(data["id"]) def get_default_scope(self): """Ensure scope is null to fit their API.""" return [""] def extract_common_fields(self, data): """Extract fields from a basic user query.""" email = None for curr_email in data.get("emails", []): email = email or curr_email.get("email") if curr_email.get("verified", False) and curr_email.get("primary", False): email = curr_email.get("email") return dict( email=email, id=data.get("id"), name=data.get("name"), first_name=data.get("first_name"), last_name=data.get("last_name"), image_url=data.get("image_url"), ) def extract_email_addresses(self, data): addresses = [] for email in data.get("emails", []): addresses.append( EmailAddress( email=email.get("email"), verified=email.get("verified"), ) ) return addresses provider_classes = [EventbriteProvider] ================================================ FILE: allauth/socialaccount/providers/eventbrite/urls.py ================================================ """Register urls for EventbriteProvider""" from allauth.socialaccount.providers.eventbrite.provider import EventbriteProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(EventbriteProvider) ================================================ FILE: allauth/socialaccount/providers/eventbrite/views.py ================================================ """Views for Eventbrite API v3.""" from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class EventbriteOAuth2Adapter(OAuth2Adapter): """OAuth2Adapter for Eventbrite API v3.""" provider_id = "eventbrite" authorize_url = "https://www.eventbrite.com/oauth/authorize" access_token_url = "https://www.eventbrite.com/oauth/token" # nosec profile_url = "https://www.eventbriteapi.com/v3/users/me/" def complete_login(self, request, app, token, **kwargs): """Complete login.""" with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, params={"token": token.token}) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(EventbriteOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(EventbriteOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/eveonline/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/eveonline/provider.py ================================================ from allauth.socialaccount.app_settings import STORE_TOKENS from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.eveonline.views import EveOnlineOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class EveOnlineAccount(ProviderAccount): def get_profile_url(self): return "https://gate.eveonline.com/Profile/{char_name}".format( char_name=self.account.extra_data.get("CharacterName") ) def get_avatar_url(self): return ("https://image.eveonline.com/Character/{char_id}_128.jpg").format( char_id=self.account.extra_data.get("CharacterID", 1) ) def to_str(self): dflt = super().to_str() return next( value for value in ( self.account.extra_data.get("CharacterName", None), self.account.extra_data.get("CharacterID", None), dflt, ) if value is not None ) class EveOnlineProvider(OAuth2Provider): id = "eveonline" name = "EVE Online" account_class = EveOnlineAccount oauth2_adapter_class = EveOnlineOAuth2Adapter def get_default_scope(self): scopes = [] if STORE_TOKENS: scopes.append("publicData") return scopes def extract_uid(self, data): return str(data["CharacterOwnerHash"]) def extract_common_fields(self, data): return dict(name=data.get("CharacterName")) provider_classes = [EveOnlineProvider] ================================================ FILE: allauth/socialaccount/providers/eveonline/urls.py ================================================ from allauth.socialaccount.providers.eveonline.provider import EveOnlineProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(EveOnlineProvider) ================================================ FILE: allauth/socialaccount/providers/eveonline/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class EveOnlineOAuth2Adapter(OAuth2Adapter): provider_id = "eveonline" access_token_url = "https://login.eveonline.com/oauth/token" # nosec authorize_url = "https://login.eveonline.com/oauth/authorize" profile_url = "https://login.eveonline.com/oauth/verify" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(EveOnlineOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(EveOnlineOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/evernote/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/evernote/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.evernote.views import EvernoteOAuthAdapter from allauth.socialaccount.providers.oauth.provider import OAuthProvider class EvernoteAccount(ProviderAccount): def get_profile_url(self): return None def get_avatar_url(self): return None class EvernoteProvider(OAuthProvider): id = "evernote" name = "Evernote" account_class = EvernoteAccount oauth_adapter_class = EvernoteOAuthAdapter def extract_uid(self, data): return str(data["edam_userId"]) def extract_common_fields(self, data): return data provider_classes = [EvernoteProvider] ================================================ FILE: allauth/socialaccount/providers/evernote/urls.py ================================================ from allauth.socialaccount.providers.evernote.provider import EvernoteProvider from allauth.socialaccount.providers.oauth.urls import default_urlpatterns urlpatterns = default_urlpatterns(EvernoteProvider) ================================================ FILE: allauth/socialaccount/providers/evernote/views.py ================================================ from datetime import datetime from allauth.socialaccount import app_settings from allauth.socialaccount.providers.oauth.views import ( OAuthAdapter, OAuthCallbackView, OAuthLoginView, ) class EvernoteOAuthAdapter(OAuthAdapter): provider_id = "evernote" settings = app_settings.PROVIDERS.get(provider_id, {}) _hostname = settings.get("EVERNOTE_HOSTNAME", "sandbox.evernote.com") request_token_url = f"https://{_hostname}/oauth" access_token_url = f"https://{_hostname}/oauth" authorize_url = f"https://{_hostname}/OAuth.action" del _hostname def complete_login(self, request, app, token, response): token.expires_at = datetime.fromtimestamp( int(response["edam_expires"]) / 1000.0 ) extra_data = response return self.get_provider().sociallogin_from_response(request, extra_data) oauth_login = OAuthLoginView.adapter_view(EvernoteOAuthAdapter) oauth_callback = OAuthCallbackView.adapter_view(EvernoteOAuthAdapter) ================================================ FILE: allauth/socialaccount/providers/exist/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/exist/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.exist.views import ExistOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class ExistAccount(ProviderAccount): def get_profile_url(self): return "https://exist.io/api/2/accounts/profile/" def get_avatar_url(self): return self.account.extra_data.get("avatar") class ExistProvider(OAuth2Provider): id = "exist" name = "Exist.io" account_class = ExistAccount oauth2_adapter_class = ExistOAuth2Adapter def extract_uid(self, data): return data.get("username") def extract_common_fields(self, data): extra_common = super().extract_common_fields(data) extra_common.update( username=data.get("username"), first_name=data.get("first_name"), last_name=data.get("last_name"), avatar=data.get("avatar"), timezone=data.get("timezone"), local_time=data.get("local_time"), ) return extra_common def get_default_scope(self): return ["mood_read", "health_read", "productivity_read"] provider_classes = [ExistProvider] ================================================ FILE: allauth/socialaccount/providers/exist/urls.py ================================================ from allauth.socialaccount.providers.exist.provider import ExistProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(ExistProvider) ================================================ FILE: allauth/socialaccount/providers/exist/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class ExistOAuth2Adapter(OAuth2Adapter): provider_id = "exist" access_token_url = "https://exist.io/oauth2/access_token" # nosec authorize_url = "https://exist.io/oauth2/authorize" profile_url = "https://exist.io/api/2/accounts/profile/" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(ExistOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(ExistOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/facebook/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/facebook/constants.py ================================================ from django.conf import settings PROVIDER_ID = "facebook" GRAPH_API_VERSION = ( getattr(settings, "SOCIALACCOUNT_PROVIDERS", {}) .get("facebook", {}) .get("VERSION", "v19.0") ) GRAPH_API_URL = ( getattr(settings, "SOCIALACCOUNT_PROVIDERS", {}) .get("facebook", {}) .get("GRAPH_API_URL", f"https://graph.facebook.com/{GRAPH_API_VERSION}") ) NONCE_SESSION_KEY = "allauth_facebook_nonce" NONCE_LENGTH = 32 ================================================ FILE: allauth/socialaccount/providers/facebook/data/FacebookLocales.xml ================================================ Afrikaans FB af_ZA Arabic FB ar_AR Azerbaijani FB az_AZ Belarusian FB be_BY Bulgarian FB bg_BG Bengali FB bn_IN Bosnian FB bs_BA Catalan FB ca_ES Czech FB cs_CZ Welsh FB cy_GB Danish FB da_DK German FB de_DE Greek FB el_GR English (UK) FB en_GB English (Pirate) FB en_PI English (Upside Down) FB en_UD English (US) FB en_US Esperanto FB eo_EO Spanish (Spain) FB es_ES Spanish FB es_LA Estonian FB et_EE Basque FB eu_ES Persian FB fa_IR Leet Speak FB fb_LT Finnish FB fi_FI Faroese FB fo_FO French (Canada) FB fr_CA French (France) FB fr_FR Frisian FB fy_NL Irish FB ga_IE Galician FB gl_ES Hebrew FB he_IL Hindi FB hi_IN Croatian FB hr_HR Hungarian FB hu_HU Armenian FB hy_AM Indonesian FB id_ID Icelandic FB is_IS Italian FB it_IT Japanese FB ja_JP Georgian FB ka_GE Khmer FB km_KH Korean FB ko_KR Kurdish FB ku_TR Latin FB la_VA Lithuanian FB lt_LT Latvian FB lv_LV Macedonian FB mk_MK Malayalam FB ml_IN Malay FB ms_MY Norwegian (bokmal) FB nb_NO Nepali FB ne_NP Dutch FB nl_NL Norwegian (nynorsk) FB nn_NO Punjabi FB pa_IN Polish FB pl_PL Pashto FB ps_AF Portuguese (Brazil) FB pt_BR Portuguese (Portugal) FB pt_PT Romanian FB ro_RO Russian FB ru_RU Slovak FB sk_SK Slovenian FB sl_SI Albanian FB sq_AL Serbian FB sr_RS Swedish FB sv_SE Swahili FB sw_KE Tamil FB ta_IN Telugu FB te_IN Thai FB th_TH Filipino FB tl_PH Turkish FB tr_TR Ukrainian FB uk_UA Vietnamese FB vi_VN Simplified Chinese (China) FB zh_CN Traditional Chinese (Hong Kong) FB zh_HK Traditional Chinese (Taiwan) FB zh_TW ================================================ FILE: allauth/socialaccount/providers/facebook/flows.py ================================================ import hashlib import hmac from datetime import timedelta from django.core.cache import cache from django.http import HttpRequest from django.utils import timezone from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.internal import jwtkit from allauth.socialaccount.models import SocialLogin, SocialToken from allauth.socialaccount.providers.base import Provider from allauth.socialaccount.providers.facebook.constants import GRAPH_API_URL # maps fields from the Limited Login JWT to Graph API response fields JWT_FIELD_TO_GRAPH_API_FIELD_MAP = { "sub": "id", "email": "email", "given_name": "first_name", "family_name": "last_name", "name": "name", "user_link": "link", } def compute_appsecret_proof(app, token) -> str: # Generate an appsecret_proof parameter to secure the Graph API call # see https://developers.facebook.com/docs/graph-api/securing-requests msg = token.token.encode("utf-8") key = app.secret.encode("utf-8") appsecret_proof = hmac.new(key, msg, digestmod=hashlib.sha256).hexdigest() return appsecret_proof def complete_login(request, provider, token): with get_adapter().get_requests_session() as sess: params = { "fields": ",".join(provider.get_fields()), "access_token": token.token, "appsecret_proof": compute_appsecret_proof(provider.app, token), } resp = sess.get(f"{GRAPH_API_URL}/me", params=params) resp.raise_for_status() extra_data = resp.json() return provider.sociallogin_from_response(request, extra_data) def get_app_token(provider): app = provider.app cache_key = f"allauth.facebook.app_token[{app.client_id}]" app_token = cache.get(cache_key) if not app_token: with get_adapter().get_requests_session() as sess: params = { "client_id": app.client_id, "client_secret": app.secret, "grant_type": "client_credentials", } resp = sess.get(f"{GRAPH_API_URL}/oauth/access_token", params=params) resp.raise_for_status() data = resp.json() app_token = data["access_token"] timeout = provider.get_settings().get("APP_TOKEN_CACHE_TIMEOUT", 300) cache.set(cache_key, app_token, timeout=timeout) return app_token def inspect_token(provider, input_token) -> None: app_token = get_app_token(provider) with get_adapter().get_requests_session() as sess: resp = sess.get( f"{GRAPH_API_URL}/debug_token", params={"input_token": input_token, "access_token": app_token}, ) resp.raise_for_status() data = resp.json()["data"] if not data["is_valid"]: raise get_adapter().validation_error("invalid_token") if data["app_id"] != provider.app.client_id or not data["is_valid"]: raise get_adapter().validation_error("invalid_token") def verify_token( request, provider: Provider, access_token: str, auth_type: str = "", auth_nonce: str = "", ) -> SocialLogin: app = provider.app inspect_token(provider, access_token) expires_at = None with get_adapter().get_requests_session() as sess: if auth_type == "reauthenticate": resp = sess.get( f"{GRAPH_API_URL}/oauth/access_token_info", params={ "client_id": app.client_id, "access_token": access_token, }, ) resp.raise_for_status() info = resp.json() ok = auth_nonce and auth_nonce == info.get("auth_nonce") if not ok: raise get_adapter().validation_error("invalid_token") if provider.get_settings().get("EXCHANGE_TOKEN"): resp = sess.get( f"{GRAPH_API_URL}/oauth/access_token", params={ "grant_type": "fb_exchange_token", "client_id": app.client_id, "client_secret": app.secret, "fb_exchange_token": access_token, }, ) resp.raise_for_status() info = resp.json() access_token = info["access_token"] expires_in = info.get("expires_in") if expires_in: expires_at = timezone.now() + timedelta(seconds=int(expires_in)) token = SocialToken(app=app, token=access_token, expires_at=expires_at) login = complete_login(request, provider, token) login.token = token return login def verify_limited_login_token( request: HttpRequest, provider, id_token: str ) -> SocialLogin: """ Verifies a Facebook Limited Login token. See https://developers.facebook.com/docs/facebook-login/limited-login/token/validating. We validate the JWT, then convert its data/claims into a fake Facebook Graph API response, which is then passed to `provider.sociallogin_from_response` to be handled as normal. """ # note: this already does replay protection internally jwt_data = jwtkit.verify_and_decode( credential=id_token, keys_url=provider.limited_login_jwks_url, issuer=provider.limited_login_expected_jwt_issuer, audience=provider.app.client_id, lookup_kid=jwtkit.lookup_kid_jwk, ) fake_response = { graph_field: jwt_data[jwt_field] for jwt_field, graph_field in JWT_FIELD_TO_GRAPH_API_FIELD_MAP.items() if jwt_field in jwt_data } return provider.sociallogin_from_response(request, fake_response) ================================================ FILE: allauth/socialaccount/providers/facebook/forms.py ================================================ from django import forms class FacebookConnectForm(forms.Form): access_token = forms.CharField(required=True) ================================================ FILE: allauth/socialaccount/providers/facebook/locale.py ================================================ # Default locale mapping for the Facebook JS SDK # The list of supported locales is at # https://www.facebook.com/translations/FacebookLocales.xml import os from django.utils.translation import get_language, to_locale def _build_locale_table(filename_or_file): """ Parses the FacebookLocales.xml file and builds a dict relating every available language ('en, 'es, 'zh', ...) with a list of available regions for that language ('en' -> 'US', 'EN') and an (arbitrary) default region. """ # Require the XML parser module only if we want the default mapping from xml.dom.minidom import parse # nosec # Trusted source dom = parse(filename_or_file) # nosec reps = dom.getElementsByTagName("representation") locs = map(lambda r: r.childNodes[0].data, reps) locale_map = {} for loc in locs: lang, _, reg = loc.partition("_") lang_map = locale_map.setdefault(lang, {"regs": [], "default": reg}) lang_map["regs"].append(reg) # Default region overrides (arbitrary) locale_map["en"]["default"] = "US" # Special case: Use es_ES for Spain and es_LA for everything else locale_map["es"]["default"] = "LA" locale_map["zh"]["default"] = "CN" locale_map["fr"]["default"] = "FR" locale_map["pt"]["default"] = "PT" return locale_map def get_default_locale_callable(): """ Wrapper function so that the default mapping is only built when needed """ exec_dir = os.path.dirname(os.path.realpath(__file__)) xml_path = os.path.join(exec_dir, "data", "FacebookLocales.xml") fb_locales = _build_locale_table(xml_path) def default_locale(request): """ Guess an appropriate FB locale based on the active Django locale. If the active locale is available, it is returned. Otherwise, it tries to return another locale with the same language. If there isn't one available, 'en_US' is returned. """ chosen = "en_US" language = get_language() if language: locale = to_locale(language) lang, _, reg = locale.partition("_") lang_map = fb_locales.get(lang) if lang_map is not None: if reg in lang_map["regs"]: chosen = f"{lang}_{reg}" else: chosen = f"{lang}_{lang_map['default']}" return chosen return default_locale ================================================ FILE: allauth/socialaccount/providers/facebook/provider.py ================================================ import requests import string from urllib.parse import quote from django.contrib.auth import REDIRECT_FIELD_NAME from django.middleware.csrf import get_token from django.template.loader import render_to_string from django.urls import reverse from django.utils.crypto import get_random_string from django.utils.html import escapejs from allauth.account.models import EmailAddress from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.app_settings import QUERY_EMAIL from allauth.socialaccount.providers.base import ( AuthAction, AuthProcess, ProviderAccount, ) from allauth.socialaccount.providers.facebook.constants import ( GRAPH_API_VERSION, NONCE_LENGTH, NONCE_SESSION_KEY, PROVIDER_ID, ) from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Error from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.utils import import_callable from .locale import get_default_locale_callable class FacebookAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("link") class FacebookProvider(OAuth2Provider): id = PROVIDER_ID name = "Facebook" account_class = FacebookAccount oauth2_adapter_class = FacebookOAuth2Adapter supports_token_authentication = True # TODO: populate these from https://www.facebook.com/.well-known/openid-configuration/ # just like in a normal OIDC provider (as that's what "Limited Login" really is) limited_login_expected_jwt_issuer = "https://www.facebook.com" limited_login_jwks_url = ( "https://limited.facebook.com/.well-known/oauth/openid/jwks/" ) def __init__(self, *args, **kwargs): self._locale_callable_cache = None super().__init__(*args, **kwargs) def get_method(self): return self.get_settings().get("METHOD", "oauth2") def get_login_url(self, request, **kwargs): method = kwargs.pop("method", self.get_method()) if method == "js_sdk": next = f"'{escapejs(kwargs.get(REDIRECT_FIELD_NAME) or '')}'" process = f"'{escapejs(kwargs.get('process') or AuthProcess.LOGIN)}'" action = f"'{escapejs(kwargs.get('action') or AuthAction.AUTHENTICATE)}'" scope = f"'{escapejs(kwargs.get('scope', ''))}'" js = f"allauth.facebook.login({next}, {action}, {process}, {scope})" ret = f"javascript:{quote(js)}" elif method == "oauth2": ret = super().get_login_url(request, **kwargs) else: raise RuntimeError(f"Invalid method specified: {method}") return ret def _get_locale_callable(self): settings = self.get_settings() func = settings.get("LOCALE_FUNC") return import_callable(func) if func else get_default_locale_callable() def get_locale_for_request(self, request): if not self._locale_callable_cache: self._locale_callable_cache = self._get_locale_callable() return self._locale_callable_cache(request) def get_default_scope(self): scope = [] if QUERY_EMAIL: scope.append("email") return scope def get_fields(self): settings = self.get_settings() default_fields = [ "id", "email", "name", "first_name", "last_name", "verified", "locale", "timezone", "link", "gender", "updated_time", ] return settings.get("FIELDS", default_fields) def get_auth_params_from_request(self, request, action): ret = super().get_auth_params_from_request(request, action) if action == AuthAction.REAUTHENTICATE: ret["auth_type"] = "reauthenticate" elif action == AuthAction.REREQUEST: ret["auth_type"] = "rerequest" return ret def get_init_params(self, request, app): init_params = {"appId": app.client_id, "version": GRAPH_API_VERSION} settings = self.get_settings() init_params.update(settings.get("INIT_PARAMS", {})) return init_params def get_fb_login_options(self, request): ret = self.get_auth_params_from_request(request, "authenticate") ret["scope"] = ",".join(self.get_scope_from_request(request)) if ret.get("auth_type") == "reauthenticate": ret["auth_nonce"] = self.get_nonce(request, or_create=True) return ret def get_sdk_url(self, request): settings = self.get_settings() sdk_url = settings.get("SDK_URL", "//connect.facebook.net/{locale}/sdk.js") field_names = [ tup[1] for tup in string.Formatter().parse(sdk_url) if tup[1] is not None ] if "locale" in field_names: locale = self.get_locale_for_request(request) sdk_url = sdk_url.format(locale=locale) return sdk_url def media_js(self, request): if self.get_method() != "js_sdk": return "" def abs_uri(name): return request.build_absolute_uri(reverse(name)) fb_data = { "appId": self.app.client_id, "version": GRAPH_API_VERSION, "sdkUrl": self.get_sdk_url(request), "initParams": self.get_init_params(request, self.app), "loginOptions": self.get_fb_login_options(request), "loginByTokenUrl": abs_uri("facebook_login_by_token"), "cancelUrl": abs_uri("socialaccount_login_cancelled"), "logoutUrl": abs_uri("account_logout"), "loginUrl": request.build_absolute_uri( self.get_login_url(request, method="oauth2") ), "errorUrl": abs_uri("socialaccount_login_error"), "csrfToken": get_token(request), } ctx = {"fb_data": fb_data} return render_to_string("facebook/fbconnect.html", ctx, request=request) def get_nonce(self, request, or_create=False, pop=False): if pop: nonce = request.session.pop(NONCE_SESSION_KEY, None) else: nonce = request.session.get(NONCE_SESSION_KEY) if not nonce and or_create: nonce = get_random_string(NONCE_LENGTH) request.session[NONCE_SESSION_KEY] = nonce return nonce def extract_uid(self, data): return data["id"] def extract_common_fields(self, data): return dict( email=data.get("email"), username=data.get("username"), first_name=data.get("first_name"), last_name=data.get("last_name"), name=data.get("name"), ) def extract_email_addresses(self, data): ret = [] email = data.get("email") if email: # data['verified'] does not imply the email address is # verified. ret.append(EmailAddress(email=email, verified=False, primary=True)) return ret def verify_token(self, request, token: dict): """ Verifies both normal oAuth2-style "access_token"s as well as OIDC-style "Limited Login" JWTs. Limited Login is an OIDC-based form of Facebook Login that their iOS SDK uses when App Tracking Transparency consent is denied. """ from allauth.socialaccount.providers.facebook import flows access_token = token.get("access_token") id_token = token.get("id_token") if not any([access_token, id_token]): raise get_adapter().validation_error("invalid_token") try: if access_token: return flows.verify_token(request, self, access_token) else: assert id_token # nosec return flows.verify_limited_login_token(request, self, id_token) except (OAuth2Error, requests.RequestException) as e: raise get_adapter().validation_error("invalid_token") from e provider_classes = [FacebookProvider] ================================================ FILE: allauth/socialaccount/providers/facebook/static/facebook/js/fbconnect.js ================================================ /* global document, window, FB */ (function () { 'use strict' function postForm (action, data) { const f = document.createElement('form') f.method = 'POST' f.action = action for (const key in data) { const d = document.createElement('input') d.type = 'hidden' d.name = key d.value = data[key] f.appendChild(d) } document.body.appendChild(f) f.submit() } function setLocationHref (url) { if (typeof (url) === 'function') { // Deprecated -- instead, override // allauth.facebook.onLoginError et al directly. url() } else { window.location.href = url } } const allauth = window.allauth = window.allauth || {} const fbSettings = JSON.parse(document.getElementById('allauth-facebook-settings').textContent) let fbInitialized = false allauth.facebook = { init: function (opts) { this.opts = opts window.fbAsyncInit = function () { FB.init(opts.initParams) fbInitialized = true allauth.facebook.onInit() }; (function (d) { const id = 'facebook-jssdk' if (d.getElementById(id)) { return } const js = d.createElement('script'); js.id = id; js.async = true js.src = opts.sdkUrl d.getElementsByTagName('head')[0].appendChild(js) }(document)) }, onInit: function () { }, login: function (nextUrl, action, process, scope) { const self = this if (!fbInitialized) { const url = this.opts.loginUrl + '?next=' + encodeURIComponent(nextUrl) + '&action=' + encodeURIComponent(action) + '&process=' + encodeURIComponent(process) + '&scope=' + encodeURIComponent(scope) setLocationHref(url) return } if (action === 'reauthenticate' || action === 'rerequest') { this.opts.loginOptions.auth_type = action } if (scope !== '') { this.opts.loginOptions.scope = scope } FB.login(function (response) { if (response.authResponse) { self.onLoginSuccess(response, nextUrl, process) } else if (response && response.status && ['not_authorized', 'unknown'].indexOf(response.status) > -1) { self.onLoginCanceled(response) } else { self.onLoginError(response) } }, self.opts.loginOptions) }, onLoginCanceled: function (/* response */) { setLocationHref(this.opts.cancelUrl) }, onLoginError: function (/* response */) { setLocationHref(this.opts.errorUrl) }, onLoginSuccess: function (response, nextUrl, process) { const data = { next: nextUrl || '', process, access_token: response.authResponse.accessToken, expires_in: response.authResponse.expiresIn, csrfmiddlewaretoken: this.opts.csrfToken } postForm(this.opts.loginByTokenUrl, data) }, logout: function (nextUrl) { const self = this if (!fbInitialized) { return } FB.logout(function (response) { self.onLogoutSuccess(response, nextUrl) }) }, onLogoutSuccess: function (response, nextUrl) { const data = { next: nextUrl || '', csrfmiddlewaretoken: this.opts.csrfToken } postForm(this.opts.logoutUrl, data) } } allauth.facebook.init(fbSettings) })() ================================================ FILE: allauth/socialaccount/providers/facebook/templates/facebook/fbconnect.html ================================================ {% load static %}
{{ fb_data|json_script:"allauth-facebook-settings" }} ================================================ FILE: allauth/socialaccount/providers/facebook/urls.py ================================================ from django.urls import path from allauth.socialaccount.providers.facebook.provider import FacebookProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from . import views urlpatterns = default_urlpatterns(FacebookProvider) urlpatterns += [ path( "facebook/login/token/", views.login_by_token, name="facebook_login_by_token", ), ] ================================================ FILE: allauth/socialaccount/providers/facebook/views.py ================================================ import logging import requests from django import forms from django.core.exceptions import PermissionDenied from django.utils.decorators import method_decorator from django.views.generic import View from allauth.account.internal.decorators import login_not_required from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.helpers import ( complete_social_login, render_authentication_error, ) from allauth.socialaccount.models import SocialLogin from allauth.socialaccount.providers.facebook import flows from allauth.socialaccount.providers.facebook.constants import ( GRAPH_API_URL, GRAPH_API_VERSION, PROVIDER_ID, ) from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) from .forms import FacebookConnectForm logger = logging.getLogger(__name__) class FacebookOAuth2Adapter(OAuth2Adapter): provider_id = PROVIDER_ID provider_default_auth_url = ( f"https://www.facebook.com/{GRAPH_API_VERSION}/dialog/oauth" ) settings = app_settings.PROVIDERS.get(provider_id, {}) scope_delimiter = "," authorize_url = settings.get("AUTHORIZE_URL", provider_default_auth_url) access_token_url = f"{GRAPH_API_URL}/oauth/access_token" access_token_method = "GET" # nosec expires_in_key = "expires_in" def complete_login(self, request, app, access_token, **kwargs): provider = self.get_provider() return flows.complete_login(request, provider, access_token) oauth2_login = OAuth2LoginView.adapter_view(FacebookOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(FacebookOAuth2Adapter) class LoginByTokenView(View): @method_decorator(login_not_required) def dispatch(self, request): self.adapter = get_adapter() self.provider = self.adapter.get_provider(request, PROVIDER_ID) try: return super().dispatch(request) except ( requests.RequestException, forms.ValidationError, PermissionDenied, ) as exc: return render_authentication_error(request, self.provider, exception=exc) def get(self, request): # If we leave out get().get() it will return a response with a 405, but # we really want to show an authentication error. raise PermissionDenied("405") def post(self, request): form = FacebookConnectForm(request.POST) if not form.is_valid(): raise self.adapter.validation_error("invalid_token") access_token = form.cleaned_data["access_token"] provider = self.provider login_options = provider.get_fb_login_options(request) auth_type = login_options.get("auth_type") auth_nonce = "" if auth_type == "reauthenticate": auth_nonce = provider.get_nonce(request, pop=True) login = flows.verify_token( request, provider, access_token, auth_type, auth_nonce ) login.state = SocialLogin.state_from_request(request) ret = complete_social_login(request, login) return ret login_by_token = LoginByTokenView.as_view() ================================================ FILE: allauth/socialaccount/providers/feedly/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/feedly/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.feedly.views import FeedlyOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class FeedlyAccount(ProviderAccount): def get_avatar_url(self): return self.account.extra_data.get("picture") class FeedlyProvider(OAuth2Provider): id = "feedly" name = "Feedly" account_class = FeedlyAccount oauth2_adapter_class = FeedlyOAuth2Adapter def get_default_scope(self): return ["https://cloud.feedly.com/subscriptions"] def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict( email=data.get("email"), last_name=data.get("familyName"), first_name=data.get("givenName"), ) provider_classes = [FeedlyProvider] ================================================ FILE: allauth/socialaccount/providers/feedly/urls.py ================================================ from allauth.socialaccount.providers.feedly.provider import FeedlyProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(FeedlyProvider) ================================================ FILE: allauth/socialaccount/providers/feedly/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class FeedlyOAuth2Adapter(OAuth2Adapter): provider_id = "feedly" host = app_settings.PROVIDERS.get(provider_id, {}).get("HOST", "cloud.feedly.com") access_token_url = f"https://{host}/v3/auth/token" authorize_url = f"https://{host}/v3/auth/auth" profile_url = f"https://{host}/v3/profile" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"OAuth {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(FeedlyOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(FeedlyOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/feishu/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/feishu/client.py ================================================ import json from collections import OrderedDict from django.utils.http import urlencode from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Client, OAuth2Error class FeishuOAuth2Client(OAuth2Client): app_access_token_url = ( "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal/" # nosec ) def get_redirect_url(self, authorization_url, scope, extra_params): scope = self.scope_delimiter.join(set(scope)) params = { "app_id": self.consumer_key, "redirect_uri": self.callback_url, "scope": scope, "response_type": "code", } if self.state: params["state"] = self.state params.update(extra_params) sorted_params = OrderedDict() for param in sorted(params): sorted_params[param] = params[param] return f"{authorization_url}?{urlencode(sorted_params)}" def app_access_token(self): data = { "app_id": self.consumer_key, "app_secret": self.consumer_secret, } self._strip_empty_keys(data) url = self.app_access_token_url # TODO: Proper exception handling with get_adapter().get_requests_session() as sess: resp = sess.request("POST", url, data=data) resp.raise_for_status() access_token = resp.json() if not access_token or "app_access_token" not in access_token: raise OAuth2Error(f"Error retrieving app access token: {resp.content}") return access_token["app_access_token"] def get_access_token(self, code, pkce_code_verifier=None): data = { "grant_type": "authorization_code", "code": code, "app_access_token": self.app_access_token(), } params = None self._strip_empty_keys(data) url = self.access_token_url if self.access_token_method == "GET": # nosec params = data data = None if data and pkce_code_verifier: data["code_verifier"] = pkce_code_verifier # TODO: Proper exception handling with get_adapter().get_requests_session() as sess: resp = sess.request( self.access_token_method, url, params=params, data=json.dumps(data), headers={"Content-Type": "application/json"}, ) resp.raise_for_status() access_token = resp.json() if ( not access_token or "data" not in access_token or "access_token" not in access_token["data"] ): raise OAuth2Error(f"Error retrieving access token: {resp.content}") return access_token["data"] ================================================ FILE: allauth/socialaccount/providers/feishu/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.feishu.views import FeishuOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class FeishuAccount(ProviderAccount): def get_avatar_url(self): return self.account.extra_data.get("avatar_big") class FeishuProvider(OAuth2Provider): id = "feishu" name = "feishu" account_class = FeishuAccount oauth2_adapter_class = FeishuOAuth2Adapter def extract_uid(self, data): return data["open_id"] def extract_common_fields(self, data): return dict(username=data.get("name"), name=data.get("name")) provider_classes = [FeishuProvider] ================================================ FILE: allauth/socialaccount/providers/feishu/urls.py ================================================ from allauth.socialaccount.providers.feishu.provider import FeishuProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(FeishuProvider) ================================================ FILE: allauth/socialaccount/providers/feishu/views.py ================================================ from django.urls import reverse from allauth.account import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Error from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) from allauth.utils import build_absolute_uri from .client import FeishuOAuth2Client class FeishuOAuth2Adapter(OAuth2Adapter): provider_id = "feishu" authorization_url = "https://open.feishu.cn/open-apis/authen/v1/index" access_token_url = ( "https://open.feishu.cn/open-apis/authen/v1/access_token" # nosec ) app_access_token_url = ( "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal/" # nosec ) user_info_url = "https://open.feishu.cn/open-apis/authen/v1/user_info" @property def authorize_url(self): settings = self.get_provider().get_settings() url = settings.get("AUTHORIZE_URL", self.authorization_url) return url def complete_login(self, request, app, token, **kwargs): headers = { "Content-Type": "application/json", "Authorization": f"Bearer {token.token}", } with get_adapter().get_requests_session() as sess: resp = sess.get(self.user_info_url, headers=headers) resp.raise_for_status() extra_data = resp.json() if extra_data["code"] != 0: raise OAuth2Error(f"Error retrieving code: {resp.content}") extra_data = extra_data["data"] return self.get_provider().sociallogin_from_response(request, extra_data) def get_client(self, request, app): callback_url = reverse(f"{self.provider_id}_callback") protocol = self.redirect_uri_protocol or app_settings.DEFAULT_HTTP_PROTOCOL callback_url = build_absolute_uri(request, callback_url, protocol=protocol) client = FeishuOAuth2Client( request, app.client_id, app.secret, self.access_token_method, self.access_token_url, callback_url, ) return client oauth2_login = OAuth2LoginView.adapter_view(FeishuOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(FeishuOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/figma/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/figma/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount import providers from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.figma.views import FigmaOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class FigmaAccount(ProviderAccount): def get_avatar_url(self): return self.account.extra_data.get("img_url", "") class FigmaProvider(OAuth2Provider): id = "figma" name = "Figma" account_class = FigmaAccount oauth2_adapter_class = FigmaOAuth2Adapter def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return { "email": data.get("email"), "name": data.get("handle"), } def extract_email_addresses(self, data): email = EmailAddress( email=data.get("email"), primary=True, verified=False, ) return [email] providers.registry.register(FigmaProvider) ================================================ FILE: allauth/socialaccount/providers/figma/urls.py ================================================ from allauth.socialaccount.providers.figma.provider import FigmaProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(FigmaProvider) ================================================ FILE: allauth/socialaccount/providers/figma/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class FigmaOAuth2Adapter(OAuth2Adapter): provider_id = "figma" authorize_url = "https://www.figma.com/oauth" access_token_url = "https://www.figma.com/api/oauth/token" # nosec userinfo_url = "https://api.figma.com/v1/me" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.userinfo_url, headers=headers) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(FigmaOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(FigmaOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/fivehundredpx/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/fivehundredpx/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/fivehundredpx/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.fivehundredpx.views import ( FiveHundredPxOAuthAdapter, ) from allauth.socialaccount.providers.oauth.provider import OAuthProvider class FiveHundredPxAccount(ProviderAccount): def get_profile_url(self): return f"https://500px.com/{self.account.extra_data.get('username')}" def get_avatar_url(self): return self.account.extra_data.get("userpic_url") class FiveHundredPxProvider(OAuthProvider): id = "500px" name = "500px" package = "allauth.socialaccount.providers.fivehundredpx" account_class = FiveHundredPxAccount oauth_adapter_class = FiveHundredPxOAuthAdapter def get_default_scope(self): return [] def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict( username=data.get("username"), email=data.get("email"), first_name=data.get("firstname"), last_name=data.get("lastname"), ) provider_classes = [FiveHundredPxProvider] ================================================ FILE: allauth/socialaccount/providers/fivehundredpx/urls.py ================================================ from allauth.socialaccount.providers.fivehundredpx.provider import FiveHundredPxProvider from allauth.socialaccount.providers.oauth.urls import default_urlpatterns urlpatterns = default_urlpatterns(FiveHundredPxProvider) ================================================ FILE: allauth/socialaccount/providers/fivehundredpx/views.py ================================================ from allauth.socialaccount.providers.oauth.client import OAuth from allauth.socialaccount.providers.oauth.views import ( OAuthAdapter, OAuthCallbackView, OAuthLoginView, ) API_BASE = "https://api.500px.com/v1" class FiveHundredPxAPI(OAuth): """ Verifying 500px credentials """ url = f"{API_BASE}/users" def get_user_info(self): return self.query(self.url).json()["user"] class FiveHundredPxOAuthAdapter(OAuthAdapter): provider_id = "500px" request_token_url = f"{API_BASE}/oauth/request_token" access_token_url = f"{API_BASE}/oauth/access_token" authorize_url = f"{API_BASE}/oauth/authorize" def complete_login(self, request, app, token, response): client = FiveHundredPxAPI( request, app.client_id, app.secret, self.request_token_url ) extra_data = client.get_user_info() return self.get_provider().sociallogin_from_response(request, extra_data) oauth_login = OAuthLoginView.adapter_view(FiveHundredPxOAuthAdapter) oauth_callback = OAuthCallbackView.adapter_view(FiveHundredPxOAuthAdapter) ================================================ FILE: allauth/socialaccount/providers/flickr/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/flickr/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.flickr.views import FlickrOAuthAdapter from allauth.socialaccount.providers.oauth.provider import OAuthProvider class FlickrAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("person").get("profileurl").get("_content") def get_avatar_url(self): return self.account.extra_data.get("picture-url") def to_str(self): username = ( self.account.extra_data.get("person", {}) .get("username", {}) .get("_content") ) if username: return username realname = ( self.account.extra_data.get("person", {}) .get("realname", {}) .get("_content") ) if realname: return realname return super().to_str() class FlickrProvider(OAuthProvider): id = "flickr" name = "Flickr" account_class = FlickrAccount oauth_adapter_class = FlickrOAuthAdapter def get_default_scope(self): scope = [] return scope def get_auth_params_from_request(self, request, action): ret = super().get_auth_params_from_request(request, action) if "perms" not in ret: ret["perms"] = "read" return ret def get_profile_fields(self): default_fields = [ "id", "first-name", "last-name", "email-address", "picture-url", "public-profile-url", ] fields = self.get_settings().get("PROFILE_FIELDS", default_fields) return fields def extract_uid(self, data): return data["person"]["nsid"] def extract_common_fields(self, data): person = data.get("person", {}) name = person.get("realname", {}).get("_content") username = person.get("username", {}).get("_content") return dict(email=data.get("email-address"), name=name, username=username) provider_classes = [FlickrProvider] ================================================ FILE: allauth/socialaccount/providers/flickr/urls.py ================================================ from allauth.socialaccount.providers.flickr.provider import FlickrProvider from allauth.socialaccount.providers.oauth.urls import default_urlpatterns urlpatterns = default_urlpatterns(FlickrProvider) ================================================ FILE: allauth/socialaccount/providers/flickr/views.py ================================================ from django.utils.http import urlencode from allauth.socialaccount.providers.oauth.client import OAuth from allauth.socialaccount.providers.oauth.views import ( OAuthAdapter, OAuthCallbackView, OAuthLoginView, ) class FlickrAPI(OAuth): api_url = "https://api.flickr.com/services/rest" def get_user_info(self): default_params = {"nojsoncallback": "1", "format": "json"} p = dict({"method": "flickr.test.login"}, **default_params) u = self.query(f"{self.api_url}?{urlencode(p)}").json() p = dict( {"method": "flickr.people.getInfo", "user_id": u["user"]["id"]}, **default_params, ) user = self.query(f"{self.api_url}?{urlencode(p)}").json() return user class FlickrOAuthAdapter(OAuthAdapter): provider_id = "flickr" request_token_url = "https://www.flickr.com/services/oauth/request_token" # nosec access_token_url = "https://www.flickr.com/services/oauth/access_token" # nosec authorize_url = "https://www.flickr.com/services/oauth/authorize" def complete_login(self, request, app, token, response): client = FlickrAPI(request, app.client_id, app.secret, self.request_token_url) extra_data = client.get_user_info() return self.get_provider().sociallogin_from_response(request, extra_data) oauth_login = OAuthLoginView.adapter_view(FlickrOAuthAdapter) oauth_callback = OAuthCallbackView.adapter_view(FlickrOAuthAdapter) ================================================ FILE: allauth/socialaccount/providers/foursquare/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/foursquare/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.foursquare.views import FoursquareOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class FoursquareAccount(ProviderAccount): def get_profile_url(self): return f"https://foursquare.com/user/{self.account.extra_data.get('id')}" def get_avatar_url(self): return self.account.extra_data.get("photo") def to_str(self): dflt = super().to_str() return self.account.extra_data.get("contact", {}).get("email", dflt) class FoursquareProvider(OAuth2Provider): id = "foursquare" name = "Foursquare" account_class = FoursquareAccount oauth2_adapter_class = FoursquareOAuth2Adapter def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict( first_name=data.get("firstname"), last_name=data.get("lastname"), email=data.get("contact").get("email"), ) provider_classes = [FoursquareProvider] ================================================ FILE: allauth/socialaccount/providers/foursquare/urls.py ================================================ from allauth.socialaccount.providers.foursquare.provider import FoursquareProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(FoursquareProvider) ================================================ FILE: allauth/socialaccount/providers/foursquare/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class FoursquareOAuth2Adapter(OAuth2Adapter): provider_id = "foursquare" access_token_url = "https://foursquare.com/oauth2/access_token" # nosec # Issue ?? -- this one authenticates over and over again... # authorize_url = 'https://foursquare.com/oauth2/authorize' authorize_url = "https://foursquare.com/oauth2/authenticate" profile_url = "https://api.foursquare.com/v2/users/self" def complete_login(self, request, app, token, **kwargs): # Foursquare needs a version number for their API requests as # documented here # https://developer.foursquare.com/overview/versioning with get_adapter().get_requests_session() as sess: params = {"oauth_token": token.token, "v": "20140116"} resp = sess.get(self.profile_url, params=params) extra_data = resp.json()["response"]["user"] return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(FoursquareOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(FoursquareOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/frontier/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/frontier/provider.py ================================================ import hashlib from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.frontier.views import FrontierOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class FrontierAccount(ProviderAccount): def get_profile_url(self): return None def get_avatar_url(self): email_hash = hashlib.sha256( self.account.extra_data.get("email").lower().encode("utf-8") ).hexdigest() return f"https://www.gravatar.com/avatar/{email_hash}?d=mp" class FrontierProvider(OAuth2Provider): id = "frontier" name = "Frontier" account_class = FrontierAccount oauth2_adapter_class = FrontierOAuth2Adapter def get_default_scope(self): scope = ["auth", "capi"] return scope def extract_uid(self, data): return str(data["customer_id"]) def extract_common_fields(self, data): return dict( email=data.get("email"), username=data.get("email"), last_name=data.get("lastname"), first_name=data.get("firstname"), ) def extract_email_addresses(self, data): ret = [] email = data.get("email") if email: ret.append(EmailAddress(email=email, verified=True, primary=True)) return ret provider_classes = [FrontierProvider] ================================================ FILE: allauth/socialaccount/providers/frontier/urls.py ================================================ from allauth.socialaccount.providers.frontier.provider import FrontierProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(FrontierProvider) ================================================ FILE: allauth/socialaccount/providers/frontier/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class FrontierOAuth2Adapter(OAuth2Adapter): provider_id = "frontier" AUTH_API = "https://auth.frontierstore.net" access_token_url = f"{AUTH_API}/token" authorize_url = f"{AUTH_API}/auth" profile_url = f"{AUTH_API}/me" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(FrontierOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(FrontierOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/fxa/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/fxa/constants.py ================================================ from django.conf import settings _FXA_SETTINGS = getattr(settings, "SOCIALACCOUNT_PROVIDERS", {}).get("fxa", {}) FXA_OAUTH_ENDPOINT = _FXA_SETTINGS.get( "OAUTH_ENDPOINT", "https://oauth.accounts.firefox.com/v1" ) FXA_PROFILE_ENDPOINT = _FXA_SETTINGS.get( "PROFILE_ENDPOINT", "https://profile.accounts.firefox.com/v1" ) PROVIDER_ID = "fxa" ================================================ FILE: allauth/socialaccount/providers/fxa/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/fxa/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.fxa.constants import PROVIDER_ID from allauth.socialaccount.providers.fxa.views import FirefoxAccountsOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class FirefoxAccountsAccount(ProviderAccount): pass class FirefoxAccountsProvider(OAuth2Provider): id = PROVIDER_ID name = "Firefox Accounts" account_class = FirefoxAccountsAccount oauth2_adapter_class = FirefoxAccountsOAuth2Adapter def get_default_scope(self): return ["profile"] def extract_uid(self, data): return str(data["uid"]) def extract_common_fields(self, data): return dict(email=data.get("email")) provider_classes = [FirefoxAccountsProvider] ================================================ FILE: allauth/socialaccount/providers/fxa/urls.py ================================================ from allauth.socialaccount.providers.fxa.provider import FirefoxAccountsProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(FirefoxAccountsProvider) ================================================ FILE: allauth/socialaccount/providers/fxa/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) from .constants import FXA_OAUTH_ENDPOINT, FXA_PROFILE_ENDPOINT, PROVIDER_ID class FirefoxAccountsOAuth2Adapter(OAuth2Adapter): provider_id = PROVIDER_ID access_token_url = f"{FXA_OAUTH_ENDPOINT}/token" authorize_url = f"{FXA_OAUTH_ENDPOINT}/authorization" profile_url = f"{FXA_PROFILE_ENDPOINT}/profile" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(FirefoxAccountsOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(FirefoxAccountsOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/gitea/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/gitea/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.gitea.views import GiteaOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class GiteaAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("html_url") def get_avatar_url(self): return self.account.extra_data.get("avatar_url") def to_str(self): dflt = super().to_str() return next( value for value in ( self.account.extra_data.get("username", None), self.account.extra_data.get("login", None), dflt, ) if value is not None ) class GiteaProvider(OAuth2Provider): id = "gitea" name = "Gitea" account_class = GiteaAccount oauth2_adapter_class = GiteaOAuth2Adapter def get_default_scope(self): scope = [] return scope def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict( email=data.get("email"), username=data.get("login"), name=data.get("name"), ) provider_classes = [GiteaProvider] ================================================ FILE: allauth/socialaccount/providers/gitea/urls.py ================================================ from allauth.socialaccount.providers.gitea.provider import GiteaProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(GiteaProvider) ================================================ FILE: allauth/socialaccount/providers/gitea/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class GiteaOAuth2Adapter(OAuth2Adapter): provider_id = "gitea" settings = app_settings.PROVIDERS.get(provider_id, {}) if "GITEA_URL" in settings: web_url = settings.get("GITEA_URL").rstrip("/") else: web_url = "https://gitea.com" api_url = f"{web_url}/api/v1" access_token_url = f"{web_url}/login/oauth/access_token" authorize_url = f"{web_url}/login/oauth/authorize" profile_url = f"{api_url}/user" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"token {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(GiteaOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(GiteaOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/github/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/github/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount import app_settings from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.github.views import GitHubOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class GitHubAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("html_url") def get_avatar_url(self): return self.account.extra_data.get("avatar_url") class GitHubProvider(OAuth2Provider): id = "github" name = "GitHub" account_class = GitHubAccount oauth2_adapter_class = GitHubOAuth2Adapter def get_default_scope(self): scope = [] if app_settings.QUERY_EMAIL: scope.append("user:email") return scope def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict( email=data.get("email"), username=data.get("login"), name=data.get("name"), ) def extract_extra_data(self, data): if "emails" in data: data = dict(data) data.pop("emails") return data def extract_email_addresses(self, data): ret = [] for email in data.get("emails", []): ret.append( EmailAddress( email=email["email"], primary=email["primary"], verified=email["verified"], ) ) return ret provider_classes = [GitHubProvider] ================================================ FILE: allauth/socialaccount/providers/github/urls.py ================================================ from allauth.socialaccount.providers.github.provider import GitHubProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(GitHubProvider) ================================================ FILE: allauth/socialaccount/providers/github/views.py ================================================ from http import HTTPStatus from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class GitHubOAuth2Adapter(OAuth2Adapter): provider_id = "github" settings = app_settings.PROVIDERS.get(provider_id, {}) if "GITHUB_URL" in settings: web_url = settings.get("GITHUB_URL").rstrip("/") api_url = f"{web_url}/api/v3" else: web_url = "https://github.com" api_url = "https://api.github.com" access_token_url = f"{web_url}/login/oauth/access_token" authorize_url = f"{web_url}/login/oauth/authorize" profile_url = f"{api_url}/user" emails_url = f"{api_url}/user/emails" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"token {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) resp.raise_for_status() extra_data = resp.json() if app_settings.QUERY_EMAIL: if emails := self.get_emails(headers): extra_data["emails"] = emails return self.get_provider().sociallogin_from_response(request, extra_data) def get_emails(self, headers) -> list | None: with get_adapter().get_requests_session() as sess: resp = sess.get(self.emails_url, headers=headers) # https://api.github.com/user/emails -- 404 is documented to occur. if resp.status_code == HTTPStatus.NOT_FOUND: return None resp.raise_for_status() return resp.json() oauth2_login = OAuth2LoginView.adapter_view(GitHubOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(GitHubOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/gitlab/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/gitlab/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.gitlab.views import GitLabOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class GitLabAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("web_url") def get_avatar_url(self): return self.account.extra_data.get("avatar_url") class GitLabProvider(OAuth2Provider): id = "gitlab" name = "GitLab" account_class = GitLabAccount oauth2_adapter_class = GitLabOAuth2Adapter def get_default_scope(self): return ["read_user"] def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict( email=data.get("email"), username=data.get("username"), name=data.get("name"), ) provider_classes = [GitLabProvider] ================================================ FILE: allauth/socialaccount/providers/gitlab/urls.py ================================================ from allauth.socialaccount.providers.gitlab.provider import GitLabProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(GitLabProvider) ================================================ FILE: allauth/socialaccount/providers/gitlab/views.py ================================================ from http import HTTPStatus from allauth.core import context from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Error from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) def _check_errors(response): # 403 error's are presented as user-facing errors if response.status_code == HTTPStatus.FORBIDDEN: msg = response.content raise OAuth2Error(f"Invalid data from GitLab API: {msg!r}") try: data = response.json() except ValueError: # JSONDecodeError on py3 raise OAuth2Error(f"Invalid JSON from GitLab API: {response.text!r}") if response.status_code >= HTTPStatus.BAD_REQUEST or "error" in data: # For errors, we expect the following format: # {"error": "error_name", "error_description": "Oops!"} # For example, if the token is not valid, we will get: # {"message": "status_code - message"} error = data.get("error", "") or response.status_code desc = data.get("error_description", "") or data.get("message", "") raise OAuth2Error(f"GitLab error: {error} ({desc})") # The expected output from the API follows this format: # {"id": 12345, ...} if "id" not in data: # If the id is not present, the output is not usable (no UID) raise OAuth2Error(f"Invalid data from GitLab API: {data!r}") return data class GitLabOAuth2Adapter(OAuth2Adapter): provider_id = "gitlab" provider_default_url = "https://gitlab.com" provider_api_version = "v4" def _build_url(self, path): settings = app_settings.PROVIDERS.get(self.provider_id, {}) gitlab_url = settings.get("GITLAB_URL", self.provider_default_url) # Prefer app based setting. app = get_adapter().get_app(context.request, provider=self.provider_id) gitlab_url = app.settings.get("gitlab_url", gitlab_url) return f"{gitlab_url}{path}" @property def access_token_url(self): return self._build_url("/oauth/token") @property def authorize_url(self): return self._build_url("/oauth/authorize") @property def profile_url(self): return self._build_url(f"/api/{self.provider_api_version}/user") def complete_login(self, request, app, token, response): with get_adapter().get_requests_session() as sess: response = sess.get(self.profile_url, params={"access_token": token.token}) data = _check_errors(response) return self.get_provider().sociallogin_from_response(request, data) oauth2_login = OAuth2LoginView.adapter_view(GitLabOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(GitLabOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/globus/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/globus/provider.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.providers.base import ProviderAccount, ProviderException from allauth.socialaccount.providers.globus.views import GlobusOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class GlobusAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("web_url", "dflt") def get_avatar_url(self): return self.account.extra_data.get("avatar_url", "dflt") class GlobusProvider(OAuth2Provider): id = "globus" name = "Globus" account_class = GlobusAccount oauth2_adapter_class = GlobusOAuth2Adapter def extract_uid(self, data): if "sub" not in data: raise ProviderException("Globus OAuth error", data) return str(data["sub"]) def extract_common_fields(self, data): return dict( email=data.get("email"), username=data.get("preferred_username"), name=data.get("name"), ) def get_default_scope(self): scope = ["openid", "profile", "offline_access"] if app_settings.QUERY_EMAIL: scope.append("email") return scope provider_classes = [GlobusProvider] ================================================ FILE: allauth/socialaccount/providers/globus/urls.py ================================================ from allauth.socialaccount.providers.globus.provider import GlobusProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(GlobusProvider) ================================================ FILE: allauth/socialaccount/providers/globus/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class GlobusOAuth2Adapter(OAuth2Adapter): provider_id = "globus" provider_default_url = "https://auth.globus.org/v2/oauth2" provider_base_url = "https://auth.globus.org/v2/oauth2" access_token_url = f"{provider_base_url}/token" authorize_url = f"{provider_base_url}/authorize" profile_url = f"{provider_base_url}/userinfo" def complete_login(self, request, app, token, response): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get( self.profile_url, params={"access_token": token.token}, headers=headers, ) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(GlobusOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(GlobusOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/google/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/google/provider.py ================================================ import requests from allauth.account.models import EmailAddress from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.app_settings import QUERY_EMAIL from allauth.socialaccount.providers.base import AuthAction, ProviderAccount from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Error from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class Scope: EMAIL = "email" PROFILE = "profile" class GoogleAccount(ProviderAccount): """ The account data can be in two formats. One, originating from the /v2/userinfo endpoint: {'email': 'john.doe@gmail.com', 'given_name': 'John', 'id': '12345678901234567890', 'locale': 'en', 'name': 'John', 'picture': 'https://lh3.googleusercontent.com/a/code', 'verified_email': True} The second, which is the payload of the id_token: {'at_hash': '-someHASH', 'aud': '123-pqr.apps.googleusercontent.com', 'azp': '123-pqr.apps.googleusercontent.com', 'email': 'john.doe@gmail.com', 'email_verified': True, 'exp': 1707297277, 'given_name': 'John', 'iat': 1707293677, 'iss': 'https://accounts.google.com', 'locale': 'en', 'name': 'John', 'picture': 'https://lh3.googleusercontent.com/a/code', 'sub': '12345678901234567890'} """ def get_profile_url(self): return self.account.extra_data.get("link") def get_avatar_url(self): return self.account.extra_data.get("picture") class GoogleProvider(OAuth2Provider): id = "google" name = "Google" account_class = GoogleAccount oauth2_adapter_class = GoogleOAuth2Adapter supports_token_authentication = True def get_default_scope(self): scope = [Scope.PROFILE] if QUERY_EMAIL: scope.append(Scope.EMAIL) return scope def get_auth_params_from_request(self, request, action): ret = super().get_auth_params_from_request(request, action) if action == AuthAction.REAUTHENTICATE: ret["prompt"] = "select_account consent" return ret def extract_uid(self, data): if "sub" in data: return data["sub"] return data["id"] def extract_common_fields(self, data): return dict( email=data.get("email"), last_name=data.get("family_name"), first_name=data.get("given_name"), ) def extract_email_addresses(self, data): ret = [] email = data.get("email") if email: verified = bool(data.get("email_verified") or data.get("verified_email")) ret.append(EmailAddress(email=email, verified=verified, primary=True)) return ret def verify_token(self, request, token): from allauth.socialaccount.providers.google import views credential = token.get("id_token") if not credential: raise get_adapter().validation_error("invalid_token") try: identity_data = views._verify_and_decode( app=self.app, credential=credential ) except (OAuth2Error, requests.RequestException) as e: raise get_adapter().validation_error("invalid_token") from e login = self.sociallogin_from_response(request, identity_data) return login provider_classes = [GoogleProvider] ================================================ FILE: allauth/socialaccount/providers/google/urls.py ================================================ from django.urls import path from allauth.socialaccount.providers.google import views from allauth.socialaccount.providers.google.provider import GoogleProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(GoogleProvider) urlpatterns += [ path( "google/login/token/", views.login_by_token, name="google_login_by_token", ), ] ================================================ FILE: allauth/socialaccount/providers/google/views.py ================================================ import requests from django.conf import settings from django.core.exceptions import PermissionDenied, ValidationError from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt from django.views.generic import View from allauth.account.internal.decorators import login_not_required from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.helpers import ( complete_social_login, render_authentication_error, ) from allauth.socialaccount.internal import jwtkit from allauth.socialaccount.providers.oauth2.client import OAuth2Error from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) CERTS_URL = ( getattr(settings, "SOCIALACCOUNT_PROVIDERS", {}) .get("google", {}) .get("CERTS_URL", "https://www.googleapis.com/oauth2/v1/certs") ) IDENTITY_URL = ( getattr(settings, "SOCIALACCOUNT_PROVIDERS", {}) .get("google", {}) .get("IDENTITY_URL", "https://www.googleapis.com/oauth2/v2/userinfo") ) ACCESS_TOKEN_URL = ( getattr(settings, "SOCIALACCOUNT_PROVIDERS", {}) .get("google", {}) .get("ACCESS_TOKEN_URL", "https://oauth2.googleapis.com/token") ) AUTHORIZE_URL = ( getattr(settings, "SOCIALACCOUNT_PROVIDERS", {}) .get("google", {}) .get("AUTHORIZE_URL", "https://accounts.google.com/o/oauth2/v2/auth") ) ID_TOKEN_ISSUER = ( getattr(settings, "SOCIALACCOUNT_PROVIDERS", {}) .get("google", {}) .get("ID_TOKEN_ISSUER", "https://accounts.google.com") ) FETCH_USERINFO = ( getattr(settings, "SOCIALACCOUNT_PROVIDERS", {}) .get("google", {}) .get("FETCH_USERINFO", False) ) def _verify_and_decode(app, credential, verify_signature=True): return jwtkit.verify_and_decode( credential=credential, keys_url=CERTS_URL, issuer=ID_TOKEN_ISSUER, audience=app.client_id, lookup_kid=jwtkit.lookup_kid_pem_x509_certificate, verify_signature=verify_signature, ) class GoogleOAuth2Adapter(OAuth2Adapter): provider_id = "google" access_token_url = ACCESS_TOKEN_URL authorize_url = AUTHORIZE_URL id_token_issuer = ID_TOKEN_ISSUER identity_url = IDENTITY_URL fetch_userinfo = FETCH_USERINFO def complete_login(self, request, app, token, response, **kwargs): data = None id_token = response.get("id_token") if id_token: data = self._decode_id_token(app, id_token) if self.fetch_userinfo and "picture" not in data: info = self._fetch_user_info(token.token) picture = info.get("picture") if picture: data["picture"] = picture else: data = self._fetch_user_info(token.token) login = self.get_provider().sociallogin_from_response(request, data) return login def _decode_id_token(self, app, id_token): """ If the token was received by direct communication protected by TLS between this library and Google, we are allowed to skip checking the token signature according to the OpenID Connect Core 1.0 specification. https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation """ verify_signature = not self.did_fetch_access_token return _verify_and_decode(app, id_token, verify_signature=verify_signature) def _fetch_user_info(self, access_token): headers = {"Authorization": f"Bearer {access_token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.identity_url, headers=headers) if not resp.ok: raise OAuth2Error("Request to user info failed") return resp.json() oauth2_login = OAuth2LoginView.adapter_view(GoogleOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(GoogleOAuth2Adapter) class LoginByTokenView(View): @method_decorator(login_not_required) def dispatch(self, request): self.adapter = get_adapter() self.provider = self.adapter.get_provider( request, GoogleOAuth2Adapter.provider_id ) try: return super().dispatch(request) except ( OAuth2Error, requests.RequestException, PermissionDenied, ValidationError, ) as exc: return render_authentication_error(request, self.provider, exception=exc) def get(self, request): # If we leave out get() it will return a response with a 405, but # we really want to show an authentication error. raise PermissionDenied("405") def post(self, request, *args, **kwargs): self.check_csrf(request) credential = request.POST.get("credential") login = self.provider.verify_token(request, {"id_token": credential}) return complete_social_login(request, login) def check_csrf(self, request): csrf_token_cookie = request.COOKIES.get("g_csrf_token") if not csrf_token_cookie: raise PermissionDenied("No CSRF token in Cookie.") csrf_token_body = request.POST.get("g_csrf_token") if not csrf_token_body: raise PermissionDenied("No CSRF token in post body.") if csrf_token_cookie != csrf_token_body: raise PermissionDenied("Failed to verify double submit cookie.") login_by_token = csrf_exempt(LoginByTokenView.as_view()) ================================================ FILE: allauth/socialaccount/providers/gumroad/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/gumroad/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.gumroad.views import GumroadOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class GumroadAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("url") class GumroadProvider(OAuth2Provider): id = "gumroad" name = "Gumroad" account_class = GumroadAccount oauth2_adapter_class = GumroadOAuth2Adapter def get_default_scope(self): return ["edit_products"] def extract_uid(self, data): return str(data["user_id"]) def extract_common_fields(self, data): try: username = data["url"].split("https://gumroad.com/")[1] except (KeyError, IndexError, AttributeError): username = None return dict( username=username, email=data.get("email"), name=data.get("name"), twitter_handle=data.get("twitter_handle"), url=data.get("url"), ) provider_classes = [GumroadProvider] ================================================ FILE: allauth/socialaccount/providers/gumroad/urls.py ================================================ from allauth.socialaccount.providers.gumroad.provider import GumroadProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(GumroadProvider) ================================================ FILE: allauth/socialaccount/providers/gumroad/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class GumroadOAuth2Adapter(OAuth2Adapter): provider_id = "gumroad" settings = app_settings.PROVIDERS.get(provider_id, {}) provider_base_url = settings.get("GUMROAD_URL") access_token_url = f"{provider_base_url}/oauth/token" authorize_url = f"{provider_base_url}/oauth/authorize" profile_url = "https://api.gumroad.com/v2/user" def complete_login(self, request, app, token, response): with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, params={"access_token": token.token}) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response( request, extra_data["user"] ) oauth2_login = OAuth2LoginView.adapter_view(GumroadOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(GumroadOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/hubic/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/hubic/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.hubic.views import HubicOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class HubicAccount(ProviderAccount): pass class HubicProvider(OAuth2Provider): id = "hubic" name = "Hubic" account_class = HubicAccount oauth2_adapter_class = HubicOAuth2Adapter def extract_uid(self, data): return str(data["email"]) def extract_common_fields(self, data): return dict( email=data.get("email"), username=data.get("firstname").lower() + data.get("lastname").lower(), first_name=data.get("firstname"), last_name=data.get("lastname"), ) provider_classes = [HubicProvider] ================================================ FILE: allauth/socialaccount/providers/hubic/urls.py ================================================ from allauth.socialaccount.providers.hubic.provider import HubicProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(HubicProvider) ================================================ FILE: allauth/socialaccount/providers/hubic/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class HubicOAuth2Adapter(OAuth2Adapter): provider_id = "hubic" access_token_url = "https://api.hubic.com/oauth/token" # nosec authorize_url = "https://api.hubic.com/oauth/auth" profile_url = "https://api.hubic.com/1.0/account" redirect_uri_protocol = "https" def complete_login(self, request, app, token, **kwargs): token_type = kwargs["response"]["token_type"] headers = {"Authorization": f"{token_type} {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(HubicOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(HubicOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/hubspot/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/hubspot/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.hubspot.views import HubspotOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class HubspotAccount(ProviderAccount): def to_str(self): return self.account.extra_data.get("user") or super().to_str() class HubspotProvider(OAuth2Provider): id = "hubspot" name = "Hubspot" account_class = HubspotAccount oauth2_adapter_class = HubspotOAuth2Adapter def get_default_scope(self): return ["oauth"] def extract_uid(self, data): return str(data["user_id"]) def extract_common_fields(self, data): return dict(email=data.get("user")) def extract_email_addresses(self, data): ret = [] email = data.get("user") if email: ret.append(EmailAddress(email=email, verified=True, primary=True)) return ret provider_classes = [HubspotProvider] ================================================ FILE: allauth/socialaccount/providers/hubspot/urls.py ================================================ from allauth.socialaccount.providers.hubspot.provider import HubspotProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(HubspotProvider) ================================================ FILE: allauth/socialaccount/providers/hubspot/views.py ================================================ """Views for Hubspot API.""" from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class HubspotOAuth2Adapter(OAuth2Adapter): """OAuth2Adapter for Hubspot API v3.""" provider_id = "hubspot" authorize_url = "https://app.hubspot.com/oauth/authorize" access_token_url = "https://api.hubapi.com/oauth/v1/token" # nosec profile_url = "https://api.hubapi.com/oauth/v1/access-tokens" def complete_login(self, request, app, token, **kwargs): headers = {"Content-Type": "application/json"} with get_adapter().get_requests_session() as sess: response = sess.get(f"{self.profile_url}/{token.token}", headers=headers) response.raise_for_status() extra_data = response.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(HubspotOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(HubspotOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/instagram/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/instagram/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.instagram.views import InstagramOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class InstagramAccount(ProviderAccount): PROFILE_URL = "https://instagram.com/" def get_profile_url(self): username = self.account.extra_data.get("username") return f"{self.PROFILE_URL}{username}" class InstagramProvider(OAuth2Provider): id = "instagram" name = "Instagram" account_class = InstagramAccount oauth2_adapter_class = InstagramOAuth2Adapter def extract_extra_data(self, data): return data def get_default_scope(self): return ["user_profile"] def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict(username=data.get("username")) provider_classes = [InstagramProvider] ================================================ FILE: allauth/socialaccount/providers/instagram/urls.py ================================================ from allauth.socialaccount.providers.instagram.provider import InstagramProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(InstagramProvider) ================================================ FILE: allauth/socialaccount/providers/instagram/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class InstagramOAuth2Adapter(OAuth2Adapter): provider_id = "instagram" access_token_url = "https://api.instagram.com/oauth/access_token" # nosec authorize_url = "https://api.instagram.com/oauth/authorize" profile_url = "https://graph.instagram.com/me" def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: params = {"access_token": token.token, "fields": ["id", "username"]} resp = sess.get(self.profile_url, params=params) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(InstagramOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(InstagramOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/jupyterhub/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/jupyterhub/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.jupyterhub.views import JupyterHubOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class JupyterHubAccount(ProviderAccount): pass class JupyterHubProvider(OAuth2Provider): id = "jupyterhub" name = "JupyterHub" account_class = JupyterHubAccount oauth2_adapter_class = JupyterHubOAuth2Adapter def extract_uid(self, data): return str(data.get("name")) def extract_common_fields(self, data): return dict(name=data.get("name", "")) provider_classes = [JupyterHubProvider] ================================================ FILE: allauth/socialaccount/providers/jupyterhub/urls.py ================================================ from allauth.socialaccount.providers.jupyterhub.provider import JupyterHubProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(JupyterHubProvider) ================================================ FILE: allauth/socialaccount/providers/jupyterhub/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.models import SocialToken from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class JupyterHubOAuth2Adapter(OAuth2Adapter): provider_id = "jupyterhub" settings = app_settings.PROVIDERS.get(provider_id, {}) provider_base_url = settings.get("API_URL", "") access_token_url = f"{provider_base_url}/hub/api/oauth2/token" authorize_url = f"{provider_base_url}/hub/api/oauth2/authorize" profile_url = f"{provider_base_url}/hub/api/user" def complete_login(self, request, app, token: SocialToken, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) user_profile = resp.json() return self.get_provider().sociallogin_from_response(request, user_profile) oauth2_login = OAuth2LoginView.adapter_view(JupyterHubOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(JupyterHubOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/kakao/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/kakao/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/kakao/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.kakao.views import KakaoOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class KakaoAccount(ProviderAccount): @property def properties(self): return self.account.extra_data.get("properties", {}) @property def profile(self): return self.account.extra_data.get("kakao_account", {}).get("profile", {}) def get_avatar_url(self): return self.profile.get( "profile_image_url", self.properties.get("profile_image") ) def get_user_data(self): return self.account.extra_data.get("kakao_account") class KakaoProvider(OAuth2Provider): id = "kakao" name = "Kakao" account_class = KakaoAccount oauth2_adapter_class = KakaoOAuth2Adapter def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): email = data.get("kakao_account", {}).get("email") nickname = data.get("kakao_account", {}).get("profile", {}).get("nickname") return dict(email=email, username=nickname) def extract_email_addresses(self, data): ret = [] data = data.get("kakao_account", {}) email = data.get("email") if email: verified = data.get("is_email_verified") # data['is_email_verified'] imply the email address is # verified ret.append(EmailAddress(email=email, verified=verified, primary=True)) return ret provider_classes = [KakaoProvider] ================================================ FILE: allauth/socialaccount/providers/kakao/urls.py ================================================ from allauth.socialaccount.providers.kakao.provider import KakaoProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(KakaoProvider) ================================================ FILE: allauth/socialaccount/providers/kakao/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class KakaoOAuth2Adapter(OAuth2Adapter): provider_id = "kakao" access_token_url = "https://kauth.kakao.com/oauth/token" # nosec authorize_url = "https://kauth.kakao.com/oauth/authorize" profile_url = "https://kapi.kakao.com/v2/user/me" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(KakaoOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(KakaoOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/lemonldap/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/lemonldap/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.lemonldap.views import LemonLDAPOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class LemonLDAPAccount(ProviderAccount): def get_avatar_url(self): return self.account.extra_data.get("picture") class LemonLDAPProvider(OAuth2Provider): id = "lemonldap" name = "LemonLDAP::NG" account_class = LemonLDAPAccount oauth2_adapter_class = LemonLDAPOAuth2Adapter def get_default_scope(self): return ["openid", "profile", "email"] def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict( email=data.get("email"), username=data.get("preferred_username"), name=data.get("name"), picture=data.get("picture"), ) provider_classes = [LemonLDAPProvider] ================================================ FILE: allauth/socialaccount/providers/lemonldap/urls.py ================================================ from allauth.socialaccount.providers.lemonldap.provider import LemonLDAPProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(LemonLDAPProvider) ================================================ FILE: allauth/socialaccount/providers/lemonldap/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.models import SocialToken from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class LemonLDAPOAuth2Adapter(OAuth2Adapter): provider_id = "lemonldap" settings = app_settings.PROVIDERS.get(provider_id, {}) provider_base_url = settings.get("LEMONLDAP_URL") access_token_url = f"{provider_base_url}/oauth2/token" authorize_url = f"{provider_base_url}/oauth2/authorize" profile_url = f"{provider_base_url}/oauth2/userinfo" def complete_login(self, request, app, token: SocialToken, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: response = sess.post(self.profile_url, headers=headers) response.raise_for_status() extra_data = response.json() extra_data["id"] = extra_data["sub"] del extra_data["sub"] return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(LemonLDAPOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(LemonLDAPOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/lichess/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/lichess/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount.app_settings import QUERY_EMAIL from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.lichess.views import LichessOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class LichessAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("url") def get_avatar_url(self): return self.account.extra_data.get("avatar") class LichessProvider(OAuth2Provider): id = "lichess" name = "Lichess" account_class = LichessAccount oauth2_adapter_class = LichessOAuth2Adapter pkce_enabled_default = True def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): first_name = data.get("profile", {}).get("firstName") last_name = data.get("profile", {}).get("lastName") return dict( username=data.get("username"), email=data.get("email"), first_name=first_name, last_name=last_name, ) def extract_email_addresses(self, data): ret = [] email = data.get("email") if email: ret.append( EmailAddress( email=email, primary=True, ) ) return ret def get_default_scope(self): ret = [] if QUERY_EMAIL: ret.append("email:read") return ret provider_classes = [LichessProvider] ================================================ FILE: allauth/socialaccount/providers/lichess/urls.py ================================================ from allauth.socialaccount.providers.lichess.provider import LichessProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(LichessProvider) ================================================ FILE: allauth/socialaccount/providers/lichess/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.app_settings import QUERY_EMAIL from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class LichessOAuth2Adapter(OAuth2Adapter): provider_id = "lichess" settings = app_settings.PROVIDERS.get(provider_id, {}) provider_base_url = settings.get("API_URL", "https://lichess.org") access_token_url = f"{provider_base_url}/api/token" authorize_url = f"{provider_base_url}/oauth" profile_url = f"{provider_base_url}/api/account" email_address_url = f"{provider_base_url}/api/account/email" def complete_login(self, request, app, token, response): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: profile_res = sess.get( self.profile_url, params={"access_token": token.token}, headers=headers, ) profile_res.raise_for_status() extra_data = profile_res.json() user_profile = ( extra_data["result"] if "result" in extra_data else extra_data ) # retrieve email address if requested if QUERY_EMAIL: email_resp = sess.get(self.email_address_url, headers=headers) email_resp.raise_for_status() email_data = email_resp.json() # extract email address from response email = email_data.get("email", None) if email: user_profile["email"] = email return self.get_provider().sociallogin_from_response(request, user_profile) oauth2_login = OAuth2LoginView.adapter_view(LichessOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(LichessOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/line/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/line/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/line/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.line.views import LineOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class LineAccount(ProviderAccount): def get_avatar_url(self): return self.account.extra_data.get("pictureUrl") or self.account.extra_data.get( "picture" ) class LineProvider(OAuth2Provider): id = "line" name = "Line" account_class = LineAccount oauth2_adapter_class = LineOAuth2Adapter def get_default_scope(self): return [] def extract_uid(self, data): return str(data.get("userId") or data.get("sub")) def extract_common_fields(self, data): return dict( email=data.get("email"), username=data.get("email") or self.extract_uid(data), first_name=data.get("first_name"), last_name=data.get("last_name"), name=data.get("name"), ) provider_classes = [LineProvider] ================================================ FILE: allauth/socialaccount/providers/line/urls.py ================================================ from allauth.socialaccount.providers.line.provider import LineProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(LineProvider) ================================================ FILE: allauth/socialaccount/providers/line/views.py ================================================ from datetime import timedelta from django.utils import timezone from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.models import SocialToken from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class LineOAuth2Adapter(OAuth2Adapter): provider_id = "line" access_token_url = "https://api.line.me/oauth2/v2.1/token" # nosec authorize_url = "https://access.line.me/oauth2/v2.1/authorize" profile_url = "https://api.line.me/v2/profile" # https://developers.line.biz/en/reference/line-login/#get-user-profile # https://developers.line.biz/en/reference/line-login/#verify-id-token id_token_url = "https://api.line.me/oauth2/v2.1/verify" # nosec def parse_token(self, data): """ data: access_token data from line """ settings = app_settings.PROVIDERS.get(self.provider_id, {}) if "email" in settings.get("SCOPE", ""): token = SocialToken(token=data["id_token"]) else: token = SocialToken(token=data["access_token"]) token.token_secret = data.get("refresh_token", "") expires_in = data.get(self.expires_in_key, None) if expires_in: token.expires_at = timezone.now() + timedelta(seconds=int(expires_in)) return token def complete_login(self, request, app, token, **kwargs): settings = app_settings.PROVIDERS.get(self.provider_id, {}) with get_adapter().get_requests_session() as sess: if "email" in settings.get("SCOPE", ""): payload = {"client_id": app.client_id, "id_token": token.token} resp = sess.post(self.id_token_url, payload) else: headers = {"Authorization": f"Bearer {token.token}"} resp = sess.get(self.profile_url, headers=headers) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(LineOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(LineOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/linkedin_oauth2/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/linkedin_oauth2/provider.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.providers.base import ProviderAccount, ProviderException from allauth.socialaccount.providers.linkedin_oauth2.views import LinkedInOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider def _extract_name_field(data, field_name): ret = "" v = data.get(field_name, {}) if v: if isinstance(v, str): # Old V1 data ret = v else: localized = v.get("localized", {}) preferred_locale = v.get( "preferredLocale", {"country": "US", "language": "en"} ) locale_key = f"{preferred_locale['language']}_{preferred_locale['country']}" if locale_key in localized: ret = localized.get(locale_key) elif localized: ret = next(iter(localized.values())) return ret def _extract_email(data): """ {'elements': [{'handle': 'urn:li:emailAddress:319371470', 'handle~': {'emailAddress': 'raymond.penners@intenct.nl'}}]} """ ret = "" elements = data.get("elements", []) if len(elements) > 0: ret = elements[0].get("handle~", {}).get("emailAddress", "") return ret class LinkedInOAuth2Account(ProviderAccount): def to_str(self): ret = super().to_str() if self.account.extra_data.get("emailAddress"): return self.account.extra_data["emailAddress"] first_name = _extract_name_field(self.account.extra_data, "firstName") last_name = _extract_name_field(self.account.extra_data, "lastName") if first_name or last_name: ret = f"{first_name} {last_name}".strip() return ret def get_avatar_url(self): """ Attempts the load the avatar associated to the avatar. Requires the `profilePicture(displayImage~:playableStreams)` profile field configured in settings.py :return: """ provider_configuration = self.account.get_provider().get_settings() configured_profile_fields = provider_configuration.get("PROFILE_FIELDS", []) # Can't get the avatar when this field is not specified picture_field = "profilePicture(displayImage~:playableStreams)" if picture_field not in configured_profile_fields: return super().get_avatar_url() # Iterate over the fields and attempt to get it by configured size profile_picture_config = provider_configuration.get("PROFILEPICTURE", {}) req_size = profile_picture_config.get("display_size_w_h", (100.0, 100.0)) req_auth_method = profile_picture_config.get("authorization_method", "PUBLIC") # Iterate over the data returned by the provider profile_elements = ( self.account.extra_data.get("profilePicture", {}) .get("displayImage~", {}) .get("elements", []) ) for single_element in profile_elements: if not req_auth_method == single_element["authorizationMethod"]: continue # Get the dimensions from the payload image_data = ( single_element.get("data", {}) .get("com.linkedin.digitalmedia.mediaartifact.StillImage", {}) .get("displaySize", {}) ) if not image_data: continue width, height = image_data["width"], image_data["height"] if not width or not height: continue if not width == req_size[0] or not height == req_size[1]: continue # Get the uri since actual size matches the requested size. to_return = single_element.get( "identifiers", [ {}, ], )[ 0 ].get("identifier") if to_return: return to_return return super().get_avatar_url() class LinkedInOAuth2Provider(OAuth2Provider): id = "linkedin_oauth2" # Name is displayed to ordinary users -- don't include protocol name = "LinkedIn" account_class = LinkedInOAuth2Account oauth2_adapter_class = LinkedInOAuth2Adapter def extract_uid(self, data): if "id" not in data: raise ProviderException( "LinkedIn encountered an internal error while logging in. \ Please try again." ) return str(data["id"]) def get_profile_fields(self): default_fields = [ "id", "firstName", "lastName", # This would be needed to in case you need access to the image # URL. Not enabling this by default due to the amount of data # returned. # # 'profilePicture(displayImage~:playableStreams)' ] fields = self.get_settings().get("PROFILE_FIELDS", default_fields) return fields def get_default_scope(self): scope = ["r_liteprofile"] if app_settings.QUERY_EMAIL: scope.append("r_emailaddress") return scope def extract_common_fields(self, data): return dict( first_name=_extract_name_field(data, "firstName"), last_name=_extract_name_field(data, "lastName"), email=_extract_email(data), ) provider_classes = [LinkedInOAuth2Provider] ================================================ FILE: allauth/socialaccount/providers/linkedin_oauth2/urls.py ================================================ from allauth.socialaccount.providers.linkedin_oauth2.provider import ( LinkedInOAuth2Provider, ) from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(LinkedInOAuth2Provider) ================================================ FILE: allauth/socialaccount/providers/linkedin_oauth2/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class LinkedInOAuth2Adapter(OAuth2Adapter): provider_id = "linkedin_oauth2" access_token_url = "https://www.linkedin.com/oauth/v2/accessToken" # nosec authorize_url = "https://www.linkedin.com/oauth/v2/authorization" profile_url = "https://api.linkedin.com/v2/me" email_url = "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))" # noqa access_token_method = "GET" # nosec def complete_login(self, request, app, token, **kwargs): extra_data = self.get_user_info(token) return self.get_provider().sociallogin_from_response(request, extra_data) def get_user_info(self, token): fields = self.get_provider().get_profile_fields() headers = { **self.get_provider().get_settings().get("HEADERS", {}), "Authorization": f"Bearer {token.token}", } info = {} with get_adapter().get_requests_session() as sess: if app_settings.QUERY_EMAIL: sess = get_adapter().get_requests_session() resp = sess.get(self.email_url, headers=headers) # If this response goes wrong, that is not a blocker in order to # continue. if resp.ok: info = resp.json() url = f"{self.profile_url}?projection=({','.join(fields)})" resp = sess.get(url, headers=headers) resp.raise_for_status() info.update(resp.json()) return info oauth2_login = OAuth2LoginView.adapter_view(LinkedInOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(LinkedInOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/mailchimp/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/mailchimp/provider.py ================================================ """Customise Provider classes for MailChimp API v3.""" from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.mailchimp.views import MailChimpOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class MailChimpAccount(ProviderAccount): """ProviderAccount subclass for MailChimp.""" def get_profile_url(self): """Return base profile url.""" return self.account.extra_data["api_endpoint"] def get_avatar_url(self): """Return avatar url.""" return self.account.extra_data["login"]["avatar"] def to_str(self): dflt = super().to_str() login_data = self.account.extra_data.get("login", {}) return login_data.get("login_email") or login_data.get("email") or dflt class MailChimpProvider(OAuth2Provider): """OAuth2Provider subclass for MailChimp v3.""" id = "mailchimp" name = "MailChimp" account_class = MailChimpAccount oauth2_adapter_class = MailChimpOAuth2Adapter def extract_uid(self, data): """Extract uid ('user_id') and ensure it's a str.""" return str(data["user_id"]) def get_default_scope(self): """Ensure scope is null to fit their API.""" return [""] def extract_common_fields(self, data): """Extract fields from a metadata query.""" return dict( dc=data.get("dc"), role=data.get("role"), account_name=data.get("accountname"), user_id=data.get("user_id"), login=data.get("login"), login_url=data.get("login_url"), api_endpoint=data.get("api_endpoint"), ) provider_classes = [MailChimpProvider] ================================================ FILE: allauth/socialaccount/providers/mailchimp/urls.py ================================================ """Register urls for MailChimpProvider""" from allauth.socialaccount.providers.mailchimp.provider import MailChimpProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(MailChimpProvider) ================================================ FILE: allauth/socialaccount/providers/mailchimp/views.py ================================================ """Views for MailChimp API v3.""" from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class MailChimpOAuth2Adapter(OAuth2Adapter): """OAuth2Adapter for MailChimp API v3.""" provider_id = "mailchimp" authorize_url = "https://login.mailchimp.com/oauth2/authorize" access_token_url = "https://login.mailchimp.com/oauth2/token" # nosec profile_url = "https://login.mailchimp.com/oauth2/metadata" def complete_login(self, request, app, token, **kwargs): """Complete login, ensuring correct OAuth header.""" headers = {"Authorization": f"OAuth {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(MailChimpOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(MailChimpOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/mailcow/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/mailcow/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.mailcow.views import MailcowAdapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class MailcowAccount(ProviderAccount): pass class MailcowProvider(OAuth2Provider): id = "mailcow" name = "Mailcow" account_class = MailcowAccount oauth2_adapter_class = MailcowAdapter def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict( username=data["username"], name=data["displayName"], full_name=data.get("full_name"), email=data["email"], ) def get_default_scope(self): scope = ["profile"] return scope provider_classes = [MailcowProvider] ================================================ FILE: allauth/socialaccount/providers/mailcow/urls.py ================================================ from allauth.socialaccount.providers.mailcow.provider import MailcowProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(MailcowProvider) ================================================ FILE: allauth/socialaccount/providers/mailcow/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) from allauth.utils import get_request_param class MailcowAdapter(OAuth2Adapter): provider_id = "mailcow" settings = app_settings.PROVIDERS.get(provider_id, {}) server = settings.get("SERVER", "https://hosted.mailcow.de") access_token_url = f"{server}/oauth/token" authorize_url = f"{server}/oauth/authorize" profile_url = f"{server}/oauth/profile" def complete_login(self, request, app, token, **kwargs): code = get_request_param(request, "code") extra_data = self.get_user_info(token.token, code) return self.get_provider().sociallogin_from_response(request, extra_data) def get_user_info(self, access_token, code): with get_adapter().get_requests_session() as sess: params = {"access_token": access_token, "code": code} resp = sess.get(self.profile_url, params=params) resp.raise_for_status() return resp.json() oauth2_login = OAuth2LoginView.adapter_view(MailcowAdapter) oauth2_callback = OAuth2CallbackView.adapter_view(MailcowAdapter) ================================================ FILE: allauth/socialaccount/providers/mailru/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/mailru/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.mailru.views import MailRuOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class MailRuAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("link") def get_avatar_url(self): ret = None if self.account.extra_data.get("has_pic"): pic_big_url = self.account.extra_data.get("pic_big") pic_small_url = self.account.extra_data.get("pic_small") if pic_big_url: return pic_big_url elif pic_small_url: return pic_small_url else: return ret class MailRuProvider(OAuth2Provider): id = "mailru" name = "Mail.RU" account_class = MailRuAccount oauth2_adapter_class = MailRuOAuth2Adapter def extract_uid(self, data): return data["uid"] def extract_common_fields(self, data): return dict( email=data.get("email"), last_name=data.get("last_name"), username=data.get("nick"), first_name=data.get("first_name"), ) provider_classes = [MailRuProvider] ================================================ FILE: allauth/socialaccount/providers/mailru/urls.py ================================================ from allauth.socialaccount.providers.mailru.provider import MailRuProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(MailRuProvider) ================================================ FILE: allauth/socialaccount/providers/mailru/views.py ================================================ from hashlib import md5 from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class MailRuOAuth2Adapter(OAuth2Adapter): provider_id = "mailru" access_token_url = "https://connect.mail.ru/oauth/token" # nosec authorize_url = "https://connect.mail.ru/oauth/authorize" profile_url = "https://www.appsmail.ru/platform/api" def complete_login(self, request, app, token, **kwargs): uid = kwargs["response"]["x_mailru_vid"] data = { "method": "users.getInfo", "app_id": app.client_id, "secure": "1", "uids": uid, } param_list = sorted([f"{item}={data[item]}" for item in data]) # See: https://api.mail.ru/docs/guides/restapi/ data["sig"] = md5( ("".join(param_list) + app.secret).encode("utf-8") ).hexdigest() # nosec with get_adapter().get_requests_session() as sess: response = sess.get(self.profile_url, params=data) extra_data = response.json()[0] return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(MailRuOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(MailRuOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/mediawiki/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/mediawiki/provider.py ================================================ from collections.abc import Mapping from typing import Any from django.conf import settings from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.mediawiki.views import MediaWikiOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider settings = getattr(settings, "SOCIALACCOUNT_PROVIDERS", {}).get("mediawiki", {}) class MediaWikiAccount(ProviderAccount): def get_profile_url(self): userpage = settings.get( "USERPAGE_TEMPLATE", "https://meta.wikimedia.org/wiki/User:{username}" ) username = self.account.extra_data.get("username") if not username: return None return userpage.format(username=username.replace(" ", "_")) class MediaWikiProvider(OAuth2Provider): id = "mediawiki" name = "MediaWiki" account_class = MediaWikiAccount oauth2_adapter_class = MediaWikiOAuth2Adapter @staticmethod def _get_email(data: Mapping[str, Any]) -> str | None: if data.get("confirmed_email"): return data.get("email") return None def extract_uid(self, data): return str(data["sub"]) def extract_extra_data(self, data: Mapping[str, Any]) -> dict[str, Any]: return dict( email=self._get_email(data), realname=data.get("realname"), username=data.get("username"), ) def extract_common_fields(self, data): return dict( email=self._get_email(data), username=data.get("username"), name=data.get("realname"), ) def extract_email_addresses(self, data: Mapping[str, Any]) -> list[EmailAddress]: # A MediaWiki account may not have email address. if addr := self._get_email(data): return [EmailAddress(email=addr, verified=True, primary=True)] return [] provider_classes = [MediaWikiProvider] ================================================ FILE: allauth/socialaccount/providers/mediawiki/urls.py ================================================ from allauth.socialaccount.providers.mediawiki.provider import MediaWikiProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(MediaWikiProvider) ================================================ FILE: allauth/socialaccount/providers/mediawiki/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class MediaWikiOAuth2Adapter(OAuth2Adapter): provider_id = "mediawiki" settings = app_settings.PROVIDERS.get(provider_id, {}) REST_API = settings.get("REST_API", "https://meta.wikimedia.org/w/rest.php") access_token_url = f"{REST_API}/oauth2/access_token" authorize_url = f"{REST_API}/oauth2/authorize" profile_url = f"{REST_API}/oauth2/resource/profile" # Allow custom User-Agent per Wikimedia policy. headers = {"User-Agent": settings.get("USER_AGENT", "django-allauth")} def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}", **self.headers} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(MediaWikiOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(MediaWikiOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/meetup/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/meetup/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/meetup/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.meetup.views import MeetupOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class MeetupAccount(ProviderAccount): pass class MeetupProvider(OAuth2Provider): id = "meetup" name = "Meetup" account_class = MeetupAccount oauth2_adapter_class = MeetupOAuth2Adapter def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict(email=data.get("email"), name=data.get("name")) provider_classes = [MeetupProvider] ================================================ FILE: allauth/socialaccount/providers/meetup/urls.py ================================================ from allauth.socialaccount.providers.meetup.provider import MeetupProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(MeetupProvider) ================================================ FILE: allauth/socialaccount/providers/meetup/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class MeetupOAuth2Adapter(OAuth2Adapter): provider_id = "meetup" access_token_url = "https://secure.meetup.com/oauth2/access" # nosec authorize_url = "https://secure.meetup.com/oauth2/authorize" profile_url = "https://api.meetup.com/2/member/self" def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, params={"access_token": token.token}) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(MeetupOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(MeetupOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/microsoft/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/microsoft/provider.py ================================================ from allauth.socialaccount.providers.base import AuthAction, ProviderAccount from allauth.socialaccount.providers.microsoft.views import MicrosoftGraphOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class MicrosoftGraphAccount(ProviderAccount): def get_avatar_url(self): return self.account.extra_data.get("photo") class MicrosoftGraphProvider(OAuth2Provider): id = "microsoft" name = "Microsoft" account_class = MicrosoftGraphAccount oauth2_adapter_class = MicrosoftGraphOAuth2Adapter def get_default_scope(self): """ Docs on Scopes and Permissions: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#scopes-and-permissions """ return ["User.Read"] def get_auth_params_from_request(self, request, action): ret = super().get_auth_params_from_request(request, action) if action == AuthAction.REAUTHENTICATE: ret["prompt"] = "select_account" return ret def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict( email=data.get("mail") or data.get("userPrincipalName"), username=data.get("mailNickname"), last_name=data.get("surname"), first_name=data.get("givenName"), ) provider_classes = [MicrosoftGraphProvider] ================================================ FILE: allauth/socialaccount/providers/microsoft/urls.py ================================================ from allauth.socialaccount.providers.microsoft.provider import MicrosoftGraphProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(MicrosoftGraphProvider) ================================================ FILE: allauth/socialaccount/providers/microsoft/views.py ================================================ import json from allauth.core import context from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Error from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) def _check_errors(response): try: data = response.json() except json.decoder.JSONDecodeError: raise OAuth2Error(f"Invalid JSON from Microsoft Graph API: {response.text}") if "id" not in data: error_message = "Error retrieving Microsoft profile" microsoft_error_message = data.get("error", {}).get("message") if microsoft_error_message: error_message = f"{error_message}: {microsoft_error_message}" raise OAuth2Error(error_message) return data class MicrosoftGraphOAuth2Adapter(OAuth2Adapter): provider_id = "microsoft" def _build_tenant_url(self, path): settings = app_settings.PROVIDERS.get(self.provider_id, {}) # Lower case "tenant" for backwards compatibility tenant = settings.get("TENANT", settings.get("tenant", "common")) # Prefer app based tenant setting. app = get_adapter().get_app(context.request, provider=self.provider_id) tenant = app.settings.get("tenant", tenant) login_url = app.settings.get("login_url", "https://login.microsoftonline.com") return f"{login_url}/{tenant}{path}" @property def access_token_url(self): return self._build_tenant_url("/oauth2/v2.0/token") @property def authorize_url(self): return self._build_tenant_url("/oauth2/v2.0/authorize") @property def profile_url(self): app = get_adapter().get_app(context.request, provider=self.provider_id) graph_url = app.settings.get("graph_url", "https://graph.microsoft.com") return f"{graph_url}/v1.0/me" user_properties = ( "businessPhones", "displayName", "givenName", "id", "jobTitle", "mail", "mobilePhone", "officeLocation", "preferredLanguage", "surname", "userPrincipalName", "mailNickname", "companyName", ) profile_url_params = {"$select": ",".join(user_properties)} def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: response = sess.get( self.profile_url, params=self.profile_url_params, headers=headers ) extra_data = _check_errors(response) return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(MicrosoftGraphOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(MicrosoftGraphOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/miro/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/miro/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.miro.views import MiroOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class MiroAccount(ProviderAccount): pass class MiroProvider(OAuth2Provider): id = "miro" name = "Miro" account_class = MiroAccount oauth2_adapter_class = MiroOAuth2Adapter def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict(email=data.get("email"), name=data.get("name")) def get_default_scope(self): return ["identity:read"] def extract_email_addresses(self, data): ret = [] email = data.get("email") if email and data.get("state") == "registered": ret.append(EmailAddress(email=email, verified=True, primary=True)) return ret provider_classes = [MiroProvider] ================================================ FILE: allauth/socialaccount/providers/miro/urls.py ================================================ from allauth.socialaccount.providers.miro.provider import MiroProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(MiroProvider) ================================================ FILE: allauth/socialaccount/providers/miro/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class MiroOAuth2Adapter(OAuth2Adapter): provider_id = "miro" access_token_url = "https://api.miro.com/v1/oauth/token" # nosec authorize_url = "https://miro.com/oauth/authorize" profile_url = "https://api.miro.com/v1/users/me" def complete_login(self, request, app, token, response): headers = { "Authorization": f"Bearer {token.token}", "Content-Type": "application/json", } with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(MiroOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(MiroOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/naver/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/naver/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/naver/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.naver.views import NaverOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class NaverAccount(ProviderAccount): def get_avatar_url(self): return self.account.extra_data.get("profile_image") class NaverProvider(OAuth2Provider): id = "naver" name = "Naver" account_class = NaverAccount oauth2_adapter_class = NaverOAuth2Adapter def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): email = data.get("email") return dict(email=email) def extract_email_addresses(self, data): ret = [] email = data.get("email") if email: ret.append(EmailAddress(email=email, verified=True, primary=True)) return ret provider_classes = [NaverProvider] ================================================ FILE: allauth/socialaccount/providers/naver/urls.py ================================================ from allauth.socialaccount.providers.naver.provider import NaverProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(NaverProvider) ================================================ FILE: allauth/socialaccount/providers/naver/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class NaverOAuth2Adapter(OAuth2Adapter): provider_id = "naver" access_token_url = "https://nid.naver.com/oauth2.0/token" # nosec authorize_url = "https://nid.naver.com/oauth2.0/authorize" profile_url = "https://openapi.naver.com/v1/nid/me" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) resp.raise_for_status() extra_data = resp.json().get("response") return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(NaverOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(NaverOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/netiq/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/netiq/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.netiq.views import NetIQOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class NetIQAccount(ProviderAccount): pass class NetIQProvider(OAuth2Provider): id = "netiq" name = "NetIQ" account_class = NetIQAccount oauth2_adapter_class = NetIQOAuth2Adapter def get_default_scope(self): return ["openid", "profile", "email"] def extract_uid(self, data): uid_field = self.app.settings.get("uid_field", "sub") return str(data[uid_field]) def extract_extra_data(self, data): return data def extract_common_fields(self, data): return dict( email=data["email"], last_name=data["family_name"], first_name=data["given_name"], ) provider_classes = [NetIQProvider] ================================================ FILE: allauth/socialaccount/providers/netiq/urls.py ================================================ from allauth.socialaccount.providers.netiq.provider import NetIQProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(NetIQProvider) ================================================ FILE: allauth/socialaccount/providers/netiq/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class NetIQOAuth2Adapter(OAuth2Adapter): provider_id = "netiq" settings = app_settings.PROVIDERS.get(provider_id, {}) provider_base_url = settings.get("NETIQ_URL") @property def access_token_url(self): return f"{self.provider_base_url}/nidp/oauth/nam/token" @property def authorize_url(self): return f"{self.provider_base_url}/nidp/oauth/nam/authz" @property def userinfo_url(self): return f"{self.provider_base_url}/nidp/oauth/nam/userinfo" def complete_login(self, request, app, token, **kwargs): """ Get the user info from userinfo endpoint and return a A populated instance of the `SocialLogin` model (unsaved) :param request: :param app: :param token: :param kwargs: :return: """ headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.userinfo_url, headers=headers) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(NetIQOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(NetIQOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/nextcloud/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/nextcloud/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.nextcloud.views import NextCloudOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class NextCloudAccount(ProviderAccount): pass class NextCloudProvider(OAuth2Provider): id = "nextcloud" name = "NextCloud" account_class = NextCloudAccount oauth2_adapter_class = NextCloudOAuth2Adapter def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict( username=data["displayname"], email=data.get("email"), ) def get_default_scope(self): scope = ["read"] return scope provider_classes = [NextCloudProvider] ================================================ FILE: allauth/socialaccount/providers/nextcloud/urls.py ================================================ from allauth.socialaccount.providers.nextcloud.provider import NextCloudProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(NextCloudProvider) ================================================ FILE: allauth/socialaccount/providers/nextcloud/views.py ================================================ from allauth.core import context from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.models import SocialToken from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class NextCloudOAuth2Adapter(OAuth2Adapter): provider_id = "nextcloud" def _build_server_url(self, path): settings = app_settings.PROVIDERS.get(self.provider_id, {}) server = settings.get("SERVER", "https://nextcloud.example.org") # Prefer app based setting. app = get_adapter().get_app(context.request, provider=self.provider_id) server = app.settings.get("server", server) ret = f"{server}{path}" return ret @property def access_token_url(self): return self._build_server_url("/apps/oauth2/api/v1/token") @property def authorize_url(self): return self._build_server_url("/apps/oauth2/authorize") @property def profile_url(self): return self._build_server_url("/ocs/v1.php/cloud/users/") def complete_login(self, request, app, token: SocialToken, **kwargs): extra_data = self.get_user_info(token, kwargs["response"]["user_id"]) return self.get_provider().sociallogin_from_response(request, extra_data) def get_user_info(self, token: SocialToken, user_id): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get( f"{self.profile_url}{user_id}", params={"format": "json"}, headers=headers, ) resp.raise_for_status() data = resp.json()["ocs"]["data"] return data oauth2_login = OAuth2LoginView.adapter_view(NextCloudOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(NextCloudOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/notion/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/notion/client.py ================================================ from http import HTTPStatus from requests.auth import HTTPBasicAuth from urllib.parse import parse_qsl from django.utils.http import urlencode from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Client, OAuth2Error class NotionOAuth2Client(OAuth2Client): def get_redirect_url(self, authorization_url, scope, extra_params) -> str: scope = self.scope_delimiter.join(set(scope)) params = { "client_id": self.consumer_key, "scope": scope, "response_type": "code", "owner": "user", } if self.state: params["state"] = self.state return f"{authorization_url}?{urlencode(params)}" def get_access_token(self, code, pkce_code_verifier=None): with get_adapter().get_requests_session() as sess: resp = sess.request( self.access_token_method, self.access_token_url, auth=HTTPBasicAuth(self.consumer_key, self.consumer_secret), json={"code": code, "grant_type": "authorization_code"}, headers=self.headers, ) access_token = None if resp.status_code in [HTTPStatus.OK, HTTPStatus.CREATED]: try: access_token = resp.json() except ValueError: access_token = dict(parse_qsl(resp.text)) if not access_token or "access_token" not in access_token: raise OAuth2Error(f"Error retrieving access token: {resp.content}") return access_token ================================================ FILE: allauth/socialaccount/providers/notion/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.notion.views import NotionOAuth2Adapter from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider class NotionAccount(ProviderAccount): def get_user(self): return self.account.extra_data["owner"]["user"] def get_name(self): return self.get_user()["name"] def get_avatar_url(self): return self.get_user()["avatar_url"] def get_workspace_name(self): return self.account.extra_data["workspace_name"] def get_workspace_icon(self): return self.account.extra_data["workspace_icon"] def to_str(self): name = self.get_name() workspace = self.get_workspace_name() return f"{name} ({workspace})" class NotionProvider(OAuth2Provider): id = "notion" name = "Notion" account_class = NotionAccount oauth2_adapter_class = NotionOAuth2Adapter def extract_uid(self, data): """ The unique identifier for Notion is a combination of the User ID and the Workspace ID they have authorized the application with. """ user_id = data["owner"]["user"]["id"] workspace_id = data["workspace_id"] return f"user-{user_id}_workspace-{workspace_id}" def extract_common_fields(self, data): user = data["owner"]["user"] user["email"] = user["person"]["email"] return user def extract_email_addresses(self, data): user = data["owner"]["user"] email = user["person"]["email"] return [EmailAddress(email=email, verified=False, primary=True)] provider_classes = [NotionProvider] ================================================ FILE: allauth/socialaccount/providers/notion/urls.py ================================================ from allauth.socialaccount.providers.notion.provider import NotionProvider from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns urlpatterns = default_urlpatterns(NotionProvider) ================================================ FILE: allauth/socialaccount/providers/notion/views.py ================================================ from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) from .client import NotionOAuth2Client class NotionOAuth2Adapter(OAuth2Adapter): provider_id = "notion" basic_auth = True client_class = NotionOAuth2Client authorize_url = "https://api.notion.com/v1/oauth/authorize" access_token_url = "https://api.notion.com/v1/oauth/token" # nosec def complete_login(self, request, app, token, **kwargs): return self.get_provider().sociallogin_from_response( request, kwargs["response"] ) oauth2_login = OAuth2LoginView.adapter_view(NotionOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(NotionOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/oauth/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/oauth/client.py ================================================ """ Parts derived from socialregistration and authorized by: alen, pinda Inspired by: https://github.com/leah/python-oauth/blob/master/oauth/example/client.py https://github.com/facebook/tornado/blob/master/tornado/auth.py """ from http import HTTPStatus from urllib.parse import parse_qsl, urlparse from django.http import HttpResponseRedirect from django.utils.http import urlencode from django.utils.translation import gettext as _ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth.oauth1_auth import OAuth1 from allauth.utils import build_absolute_uri, get_request_param def get_token_prefix(url) -> str: """ Returns a prefix for the token to store in the session so we can hold more than one single oauth provider's access key in the session. Example: The request token url ``https://x.com/oauth/request_token`` returns ``x.com`` """ return urlparse(url).netloc class OAuthError(Exception): pass class OAuthClient: def __init__( self, request, consumer_key, consumer_secret, request_token_url, access_token_url, callback_url, parameters=None, provider=None, ): self.request = request self.request_token_url = request_token_url self.access_token_url = access_token_url self.consumer_key = consumer_key self.consumer_secret = consumer_secret self.parameters = parameters self.callback_url = callback_url self.provider = provider self.errors = [] self.request_token = None self.access_token = None def _get_request_token(self): """ Obtain a temporary request token to authorize an access token and to sign the request to obtain the access token """ if self.request_token is None: get_params = {} if self.parameters: get_params.update(self.parameters) get_params["oauth_callback"] = build_absolute_uri( self.request, self.callback_url ) rt_url = f"{self.request_token_url}?{urlencode(get_params)}" oauth = OAuth1(self.consumer_key, client_secret=self.consumer_secret) with get_adapter().get_requests_session() as sess: response = sess.post(url=rt_url, auth=oauth) if response.status_code not in [HTTPStatus.OK, HTTPStatus.CREATED]: raise OAuthError( _( "Invalid response while obtaining request token" ' from "%s". Response was: %s.' ) % (get_token_prefix(self.request_token_url), response.text) ) self.request_token = dict(parse_qsl(response.text)) self.request.session[ f"oauth_{get_token_prefix(self.request_token_url)}_request_token" ] = self.request_token return self.request_token def get_access_token(self): """ Obtain the access token to access private resources at the API endpoint. """ if self.access_token is None: request_token = self._get_rt_from_session() oauth = OAuth1( self.consumer_key, client_secret=self.consumer_secret, resource_owner_key=request_token["oauth_token"], resource_owner_secret=request_token["oauth_token_secret"], ) at_url = self.access_token_url # Passing along oauth_verifier is required according to: # https://groups.google.com/group/twitter-development-talk/browse_frm/thread/472500cfe9e7cdb9# # Though, the custom oauth_callback seems to work without it? oauth_verifier = get_request_param(self.request, "oauth_verifier") if oauth_verifier: at_url = f"{at_url}?{urlencode({'oauth_verifier': oauth_verifier})}" with get_adapter().get_requests_session() as sess: response = sess.post(url=at_url, auth=oauth) if response.status_code not in [HTTPStatus.OK, HTTPStatus.CREATED]: raise OAuthError( _('Invalid response while obtaining access token from "%s".') % get_token_prefix(self.request_token_url) ) self.access_token = dict(parse_qsl(response.text)) self.request.session[ f"oauth_{get_token_prefix(self.request_token_url)}_access_token" ] = self.access_token return self.access_token def _get_rt_from_session(self): """ Returns the request token cached in the session by ``_get_request_token`` """ try: return self.request.session[ f"oauth_{get_token_prefix(self.request_token_url)}_request_token" ] except KeyError: raise OAuthError( _('No request token saved for "%s".') % get_token_prefix(self.request_token_url) ) def is_valid(self) -> bool: try: self._get_rt_from_session() self.get_access_token() except OAuthError as e: self.errors.append(e.args[0]) return False return True def get_redirect(self, authorization_url, extra_params): """ Returns a ``HttpResponseRedirect`` object to redirect the user to the URL the OAuth provider handles authorization. """ request_token = self._get_request_token() params = { "oauth_token": request_token["oauth_token"], "oauth_callback": self.request.build_absolute_uri(self.callback_url), } params.update(extra_params) url = f"{authorization_url}?{urlencode(params)}" return HttpResponseRedirect(url) class OAuth: """ Base class to perform oauth signed requests from access keys saved in a user's session. See the ``OAuthTwitter`` class below for an example. """ def __init__(self, request, consumer_key, secret_key, request_token_url): self.request = request self.consumer_key = consumer_key self.secret_key = secret_key self.request_token_url = request_token_url def _get_at_from_session(self): """ Get the saved access token for private resources from the session. """ try: return self.request.session[ f"oauth_{get_token_prefix(self.request_token_url)}_access_token" ] except KeyError: raise OAuthError( _('No access token saved for "%s".') % get_token_prefix(self.request_token_url) ) def query(self, url, method="GET", params=None, headers=None): """ Request a API endpoint at ``url`` with ``params`` being either the POST or GET data. """ access_token = self._get_at_from_session() oauth = OAuth1( self.consumer_key, client_secret=self.secret_key, resource_owner_key=access_token["oauth_token"], resource_owner_secret=access_token["oauth_token_secret"], ) with get_adapter().get_requests_session() as sess: response = sess.request( url, method=method.lower(), auth=oauth, headers=headers, params=params ) if response.status_code != HTTPStatus.OK: raise OAuthError( _('No access to private resources at "%s".') % get_token_prefix(self.request_token_url) ) return response ================================================ FILE: allauth/socialaccount/providers/oauth/oauth1_auth.py ================================================ # This file is from the requests-oauthlib library, # https://github.com/requests/requests-oauthlib/blob/416d7382a193d180aac3a2fb7da6f621401ac960/requests_oauthlib/oauth1_auth.py # # The requests-oauthlib library is licensed under the ISC License: # # ISC License # # Copyright (c) 2014 Kenneth Reitz. # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import logging from requests.auth import AuthBase from requests.utils import to_native_string from oauthlib.common import extract_params from oauthlib.oauth1 import ( SIGNATURE_HMAC, SIGNATURE_TYPE_AUTH_HEADER, SIGNATURE_TYPE_BODY, Client, ) CONTENT_TYPE_FORM_URLENCODED = "application/x-www-form-urlencoded" CONTENT_TYPE_MULTI_PART = "multipart/form-data" log = logging.getLogger(__name__) # OBS!: Correct signing of requests are conditional on invoking OAuth1 # as the last step of preparing a request, or at least having the # content-type set properly. class OAuth1(AuthBase): """Signs the request using OAuth 1 (RFC5849)""" client_class = Client def __init__( self, client_key, client_secret=None, resource_owner_key=None, resource_owner_secret=None, callback_uri=None, signature_method=SIGNATURE_HMAC, signature_type=SIGNATURE_TYPE_AUTH_HEADER, rsa_key=None, verifier=None, decoding="utf-8", client_class=None, force_include_body=False, **kwargs, ): try: signature_type = signature_type.upper() except AttributeError: pass client_class = client_class or self.client_class self.force_include_body = force_include_body self.client = client_class( client_key, client_secret, resource_owner_key, resource_owner_secret, callback_uri, signature_method, signature_type, rsa_key, verifier, decoding=decoding, **kwargs, ) def __call__(self, r): """Add OAuth parameters to the request. Parameters may be included from the body if the content-type is urlencoded, if no content type is set a guess is made. """ # Overwriting url is safe here as request will not modify it past # this point. log.debug("Signing request %s using client %s", r, self.client) content_type = r.headers.get("Content-Type", "") if ( not content_type and extract_params(r.body) or self.client.signature_type == SIGNATURE_TYPE_BODY ): content_type = CONTENT_TYPE_FORM_URLENCODED if not isinstance(content_type, str): content_type = content_type.decode("utf-8") is_form_encoded = CONTENT_TYPE_FORM_URLENCODED in content_type log.debug( "Including body in call to sign: %s", is_form_encoded or self.force_include_body, ) if is_form_encoded: r.headers["Content-Type"] = CONTENT_TYPE_FORM_URLENCODED r.url, headers, r.body = self.client.sign( str(r.url), str(r.method), r.body or "", r.headers ) elif self.force_include_body: # To allow custom clients to work on non form encoded bodies. r.url, headers, r.body = self.client.sign( str(r.url), str(r.method), r.body or "", r.headers ) else: # Omit body data in the signing of non form-encoded requests r.url, headers, _ = self.client.sign( str(r.url), str(r.method), None, r.headers ) r.prepare_headers(headers) r.url = to_native_string(r.url) log.debug("Updated url: %s", r.url) log.debug("Updated headers: %s", headers) log.debug("Updated body: %r", r.body) return r ================================================ FILE: allauth/socialaccount/providers/oauth/provider.py ================================================ import logging from urllib.parse import parse_qsl from django.core.exceptions import ImproperlyConfigured from django.urls import reverse from django.utils.http import urlencode from allauth.socialaccount.helpers import render_authentication_error from allauth.socialaccount.providers.base import Provider from allauth.socialaccount.providers.base.constants import AuthAction from allauth.socialaccount.providers.oauth.client import OAuthError logger = logging.getLogger(__name__) class OAuthProvider(Provider): supports_redirect = True def get_login_url(self, request, **kwargs): url = reverse(f"{self.id}_login") if kwargs: url = f"{url}?{urlencode(kwargs)}" return url def get_auth_params(self): settings = self.get_settings() ret = dict(settings.get("AUTH_PARAMS", {})) return ret def get_auth_params_from_request(self, request, action): ret = self.get_auth_params() dynamic_auth_params = request.GET.get("auth_params", None) if dynamic_auth_params: ret.update(dict(parse_qsl(dynamic_auth_params))) return ret def get_auth_url(self, request, action): # TODO: This is ugly. Move authorization_url away from the # adapter into the provider. Hmpf, the line between # adapter/provider is a bit too thin here. return None def get_scope_from_request(self, request): return self.get_scope() def get_scope(self): settings = self.get_settings() scope = settings.get("SCOPE") if scope is None: scope = self.get_default_scope() return scope def get_default_scope(self): return [] def get_oauth_adapter(self, request): if not hasattr(self, "oauth_adapter_class"): raise ImproperlyConfigured(f"No oauth_adapter_class set for {self!r}") return self.oauth_adapter_class(request) def get_redirect_from_request_kwargs(self, request): kwargs = super().get_redirect_from_request_kwargs(request) kwargs["scope"] = self.get_scope_from_request(request) action = request.GET.get("action", AuthAction.AUTHENTICATE) kwargs["action"] = action kwargs["auth_params"] = self.get_auth_params_from_request(request, action) return kwargs def redirect(self, request, process, next_url=None, data=None, **kwargs): callback_url = reverse(f"{self.id}_callback") oauth_adapter = self.get_oauth_adapter(request) action = kwargs.pop("action", AuthAction.AUTHENTICATE) auth_url = self.get_auth_url(request, action) or oauth_adapter.authorize_url auth_params = kwargs.pop("auth_params", None) if auth_params is None: auth_params = self.get_auth_params() scope = kwargs.pop("scope", None) if scope is None: scope = self.get_scope() self.stash_redirect_state(request, process, next_url, data, **kwargs) client = oauth_adapter._get_client(request, callback_url, scope=scope) try: return client.get_redirect(auth_url, auth_params) except OAuthError as e: logger.error("OAuth authentication error", exc_info=True) return render_authentication_error(request, self, exception=e) ================================================ FILE: allauth/socialaccount/providers/oauth/urls.py ================================================ from django.urls import include, path from allauth.utils import import_attribute def default_urlpatterns(provider): login_view = import_attribute(f"{provider.get_package()}.views.oauth_login") callback_view = import_attribute(f"{provider.get_package()}.views.oauth_callback") urlpatterns = [ path("login/", login_view, name=f"{provider.id}_login"), path("login/callback/", callback_view, name=f"{provider.id}_callback"), ] return [path(f"{provider.get_slug()}/", include(urlpatterns))] ================================================ FILE: allauth/socialaccount/providers/oauth/views.py ================================================ import logging from django.http import HttpRequest from django.urls import reverse from allauth.account.internal.decorators import login_not_required from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.helpers import ( complete_social_login, render_authentication_error, ) from allauth.socialaccount.models import SocialLogin, SocialToken from allauth.socialaccount.providers.base.constants import AuthError from allauth.socialaccount.providers.base.views import BaseLoginView from allauth.socialaccount.providers.oauth.client import OAuthClient, OAuthError logger = logging.getLogger(__name__) class OAuthAdapter: client_class = OAuthClient def __init__(self, request) -> None: self.request = request def complete_login(self, request, app): """ Returns a SocialLogin instance """ raise NotImplementedError def get_provider(self): adapter = get_adapter(self.request) app = adapter.get_app(self.request, provider=self.provider_id) return app.get_provider(self.request) def _get_client(self, request, callback_url, scope=None): provider = self.get_provider() app = provider.app parameters = {} if scope: parameters["scope"] = " ".join(scope) client = self.client_class( request, app.client_id, app.secret, self.request_token_url, self.access_token_url, callback_url, parameters=parameters, provider=provider, ) return client class OAuthView: request: HttpRequest @classmethod def adapter_view(cls, adapter): @login_not_required def view(request, *args, **kwargs): self = cls() self.request = request self.adapter = adapter(request) return self.dispatch(request, *args, **kwargs) return view class OAuthLoginView(OAuthView, BaseLoginView): def get_provider(self): provider = self.adapter.get_provider() return provider class OAuthCallbackView(OAuthView): def dispatch(self, request): """ View to handle final steps of OAuth based authentication where the user gets redirected back to from the service provider """ provider = self.adapter.get_provider() login_done_url = reverse(f"{self.adapter.provider_id}_callback") client = self.adapter._get_client(request, login_done_url) if not client.is_valid(): if "denied" in request.GET: error = AuthError.CANCELLED else: error = AuthError.UNKNOWN return render_authentication_error( request, provider, error=error, extra_context={ "oauth_client": client, "callback_view": self, }, ) app = provider.app try: access_token = client.get_access_token() token = SocialToken( token=access_token["oauth_token"], # .get() -- e.g. Evernote does not feature a secret token_secret=access_token.get("oauth_token_secret", ""), ) if app.pk: token.app = app login = self.adapter.complete_login( request, app, token, response=access_token ) login.token = token login.state = SocialLogin.unstash_state(request) return complete_social_login(request, login) except OAuthError as e: return render_authentication_error(request, provider, exception=e) ================================================ FILE: allauth/socialaccount/providers/oauth2/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/oauth2/client.py ================================================ import requests from http import HTTPStatus from urllib.parse import parse_qsl from django.utils.http import urlencode from allauth.socialaccount.adapter import get_adapter class OAuth2Error(Exception): pass class OAuth2Client: client_id_parameter = "client_id" def __init__( self, request, consumer_key, consumer_secret, access_token_method, access_token_url, callback_url, scope_delimiter=" ", headers=None, basic_auth=False, ): self.request = request self.access_token_method = access_token_method self.access_token_url = access_token_url self.callback_url = callback_url self.consumer_key = consumer_key self.consumer_secret = consumer_secret self.scope_delimiter = scope_delimiter self.state = None self.headers = headers self.basic_auth = basic_auth def get_redirect_url(self, authorization_url, scope, extra_params) -> str: scope = self.scope_delimiter.join(set(scope)) params = { self.client_id_parameter: self.consumer_key, "redirect_uri": self.callback_url, "scope": scope, "response_type": "code", } if self.state: params["state"] = self.state params.update(extra_params) return f"{authorization_url}?{urlencode(params)}" def get_access_token(self, code, pkce_code_verifier=None, extra_data=None): data = { "redirect_uri": self.callback_url, "grant_type": "authorization_code", "code": code, } if self.basic_auth: auth = requests.auth.HTTPBasicAuth(self.consumer_key, self.consumer_secret) else: auth = None data.update( { self.client_id_parameter: self.consumer_key, "client_secret": self.consumer_secret, } ) if extra_data: data.update(extra_data) params = None self._strip_empty_keys(data) url = self.access_token_url if self.access_token_method == "GET": # nosec params = data data = None if data and pkce_code_verifier: data["code_verifier"] = pkce_code_verifier # TODO: Proper exception handling with get_adapter().get_requests_session() as sess: resp = sess.request( self.access_token_method, url, params=params, data=data, headers=self.headers, auth=auth, ) access_token = None if resp.status_code in [HTTPStatus.OK, HTTPStatus.CREATED]: # Weibo sends json via 'text/plain;charset=UTF-8' if ( resp.headers["content-type"].split(";")[0] == "application/json" or resp.text[:2] == '{"' ): access_token = resp.json() else: access_token = dict(parse_qsl(resp.text)) if not access_token or "access_token" not in access_token: raise OAuth2Error(f"Error retrieving access token: {resp.content}") return access_token def _strip_empty_keys(self, params) -> None: """Added because the Dropbox OAuth2 flow doesn't work when scope is passed in, which is empty. """ keys = [k for k, v in params.items() if v == ""] for key in keys: del params[key] ================================================ FILE: allauth/socialaccount/providers/oauth2/provider.py ================================================ from urllib.parse import parse_qsl from django.core.exceptions import ImproperlyConfigured from django.http import HttpResponseRedirect from django.urls import reverse from django.utils.http import urlencode from allauth.socialaccount.helpers import render_authentication_error from allauth.socialaccount.providers.base import Provider from allauth.socialaccount.providers.base.constants import AuthAction from allauth.socialaccount.providers.oauth2.client import OAuth2Error from allauth.socialaccount.providers.oauth2.utils import generate_code_challenge from allauth.socialaccount.providers.oauth2.views import OAuth2Adapter class OAuth2Provider(Provider): pkce_enabled_default = False oauth2_adapter_class: type[OAuth2Adapter] supports_redirect = True def get_login_url(self, request, **kwargs): url = reverse(f"{self.id}_login") if kwargs: url = f"{url}?{urlencode(kwargs)}" return url def get_callback_url(self): return reverse(f"{self.id}_callback") def get_pkce_params(self) -> dict: enabled = self.app.settings.get("oauth_pkce_enabled") if enabled is None: settings = self.get_settings() enabled = settings.get("OAUTH_PKCE_ENABLED", self.pkce_enabled_default) if enabled: pkce_code_params = generate_code_challenge() return pkce_code_params return {} def get_auth_params(self): """ Returns a dictionary of additional parameters passed to the OAuth2 redirect URL. Additional -- so no need to pass the standard `client_id`, `redirect_uri`, `response_type`. """ ret = self.app.settings.get("auth_params") if ret is None: settings = self.get_settings() ret = settings.get("AUTH_PARAMS", {}) return dict(ret) def get_auth_params_from_request(self, request, action): """ Returns a dictionary of additional parameters passed to the OAuth2 redirect URL. Additional -- so no need to pass the standard `client_id`, `redirect_uri`, `response_type`. """ ret = self.get_auth_params() dynamic_auth_params = request.GET.get("auth_params", None) if dynamic_auth_params: ret.update(dict(parse_qsl(dynamic_auth_params))) return ret def get_default_scope(self): """ Returns the default scope to use. """ return [] def get_scope(self): """ Returns the scope to use, taking settings `SCOPE` into consideration. """ scope = self.app.settings.get("scope") if scope is None: settings = self.get_settings() scope = settings.get("SCOPE", self.get_default_scope()) return list(scope) def get_scope_from_request(self, request): """ Returns the scope to use for the given request. """ scope = self.get_scope() dynamic_scope = request.GET.get("scope", None) if dynamic_scope: scope.extend(dynamic_scope.split(",")) return scope def get_oauth2_adapter(self, request): if not hasattr(self, "oauth2_adapter_class"): raise ImproperlyConfigured(f"No oauth2_adapter_class set for {self!r}") return self.oauth2_adapter_class(request) def get_redirect_from_request_kwargs(self, request): kwargs = super().get_redirect_from_request_kwargs(request) kwargs["scope"] = self.get_scope_from_request(request) action = request.GET.get("action", AuthAction.AUTHENTICATE) kwargs["auth_params"] = self.get_auth_params_from_request(request, action) return kwargs def redirect(self, request, process, next_url=None, data=None, **kwargs): app = self.app oauth2_adapter = self.get_oauth2_adapter(request) client = oauth2_adapter.get_client(request, app) auth_params = kwargs.pop("auth_params", None) if auth_params is None: auth_params = self.get_auth_params() pkce_params = self.get_pkce_params() code_verifier = pkce_params.pop("code_verifier", None) auth_params.update(pkce_params) scope = kwargs.pop("scope", None) if scope is None: scope = self.get_scope() state_id = self.stash_redirect_state( request, process, next_url, data, pkce_code_verifier=code_verifier, **kwargs ) client.state = state_id try: return HttpResponseRedirect( client.get_redirect_url( oauth2_adapter.authorize_url, scope, auth_params ) ) except OAuth2Error as e: return render_authentication_error( request, self, extra_context={"state_id": state_id}, exception=e ) ================================================ FILE: allauth/socialaccount/providers/oauth2/urls.py ================================================ from django.urls import include, path from allauth.utils import import_attribute def default_urlpatterns(provider): login_view = import_attribute(f"{provider.get_package()}.views.oauth2_login") callback_view = import_attribute(f"{provider.get_package()}.views.oauth2_callback") urlpatterns = [ path("login/", login_view, name=f"{provider.id}_login"), path("login/callback/", callback_view, name=f"{provider.id}_callback"), ] return [path(f"{provider.get_slug()}/", include(urlpatterns))] ================================================ FILE: allauth/socialaccount/providers/oauth2/utils.py ================================================ import base64 import hashlib from secrets import token_urlsafe def generate_code_challenge() -> dict: # Create a code verifier with a length of 128 characters code_verifier = token_urlsafe(96) hashed_verifier = hashlib.sha256(code_verifier.encode("ascii")) code_challenge = base64.urlsafe_b64encode(hashed_verifier.digest()) code_challenge_without_padding = code_challenge.rstrip(b"=") return { "code_verifier": code_verifier, "code_challenge_method": "S256", "code_challenge": code_challenge_without_padding, } ================================================ FILE: allauth/socialaccount/providers/oauth2/views.py ================================================ from datetime import timedelta from requests import RequestException from django.conf import settings from django.core.exceptions import PermissionDenied from django.http import HttpRequest from django.shortcuts import render from django.urls import reverse from django.utils import timezone from allauth.account import app_settings as account_settings from allauth.account.internal.decorators import login_not_required from allauth.core.exceptions import ImmediateHttpResponse from allauth.core.internal.httpkit import add_query_params from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.helpers import ( complete_social_login, render_authentication_error, ) from allauth.socialaccount.internal import statekit from allauth.socialaccount.models import SocialToken from allauth.socialaccount.providers.base import ProviderException from allauth.socialaccount.providers.base.constants import AuthError from allauth.socialaccount.providers.base.views import BaseLoginView from allauth.socialaccount.providers.oauth2.client import OAuth2Client, OAuth2Error from allauth.utils import build_absolute_uri, get_request_param class OAuth2Adapter: expires_in_key = "expires_in" client_class = OAuth2Client supports_state = True redirect_uri_protocol: str | None = None access_token_method = "POST" # nosec login_cancelled_error = "access_denied" scope_delimiter = " " basic_auth = False headers: dict[str, str] | None = None def __init__(self, request) -> None: self.request = request self.did_fetch_access_token = False def get_provider(self): return get_adapter(self.request).get_provider( self.request, provider=self.provider_id ) def complete_login(self, request, app, token: SocialToken, **kwargs): """ Returns a SocialLogin instance """ raise NotImplementedError def get_callback_url(self, request, app): callback_url = reverse(f"{self.provider_id}_callback") protocol = self.redirect_uri_protocol return build_absolute_uri(request, callback_url, protocol) def parse_token(self, data): token = SocialToken(token=data["access_token"]) token.token_secret = data.get("refresh_token", "") expires_in = data.get(self.expires_in_key, None) if expires_in: token.expires_at = timezone.now() + timedelta(seconds=int(expires_in)) return token def get_access_token_data(self, request, app, client, pkce_code_verifier=None): code = get_request_param(self.request, "code") data = client.get_access_token(code, pkce_code_verifier=pkce_code_verifier) self.did_fetch_access_token = True return data def get_client(self, request, app): callback_url = self.get_callback_url(request, app) client = self.client_class( self.request, app.client_id, app.secret, self.access_token_method, self.access_token_url, callback_url, scope_delimiter=self.scope_delimiter, headers=self.headers, basic_auth=self.basic_auth, ) return client class OAuth2View: request: HttpRequest @classmethod def adapter_view(cls, adapter): @login_not_required def view(request, *args, **kwargs): self = cls() self.request = request if not isinstance(adapter, OAuth2Adapter): self.adapter = adapter(request) else: self.adapter = adapter try: return self.dispatch(request, *args, **kwargs) except ImmediateHttpResponse as e: return e.response return view class OAuth2LoginView(OAuth2View, BaseLoginView): def get_provider(self): return self.adapter.get_provider() class OAuth2CallbackView(OAuth2View): def dispatch(self, request, *args, **kwargs): provider = self.adapter.get_provider() state, resp = self._get_state(request, provider) if resp: return resp if "error" in request.GET or "code" not in request.GET: # Distinguish cancel from error auth_error = request.GET.get("error", None) if auth_error == self.adapter.login_cancelled_error: error = AuthError.CANCELLED else: error = AuthError.UNKNOWN return render_authentication_error( request, provider, error=error, extra_context={ "state": state, "callback_view": self, }, ) app = provider.app client = self.adapter.get_client(self.request, app) try: access_token = self.adapter.get_access_token_data( request, app, client, pkce_code_verifier=state.get("pkce_code_verifier") ) token = self.adapter.parse_token(access_token) if app.pk: token.app = app login = self.adapter.complete_login( request, app, token, response=access_token ) login.token = token login.state = state return complete_social_login(request, login) except ( PermissionDenied, OAuth2Error, RequestException, ProviderException, ) as e: return render_authentication_error( request, provider, exception=e, extra_context={"state": state} ) def _redirect_strict_samesite(self, request, provider): if ( "_redir" in request.GET or settings.SESSION_COOKIE_SAMESITE.lower() != "strict" or request.method != "GET" ): return redirect_to = request.get_full_path() redirect_to = add_query_params(redirect_to, {"_redir": ""}) return render( request, f"socialaccount/login_redirect.{account_settings.TEMPLATE_EXTENSION}", { "provider": provider, "redirect_to": redirect_to, }, ) def _get_state(self, request, provider): state = None state_id = get_request_param(request, "state") if self.adapter.supports_state: if state_id: state = statekit.unstash_state(request, state_id) else: state = statekit.unstash_last_state(request) if state is None: resp = self._redirect_strict_samesite(request, provider) if resp: # 'Strict' is in effect, let's try a redirect and then another # shot at finding our state... return None, resp return None, render_authentication_error( request, provider, extra_context={ "state_id": state_id, "callback_view": self, }, ) return state, None ================================================ FILE: allauth/socialaccount/providers/odnoklassniki/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/odnoklassniki/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.odnoklassniki.views import ( OdnoklassnikiOAuth2Adapter, ) class OdnoklassnikiAccount(ProviderAccount): def get_profile_url(self): return f"https://ok.ru/profile/{self.account.extra_data['uid']}" def get_avatar_url(self): ret = None pic_big_url = self.account.extra_data.get("pic1024x768") pic_medium_url = self.account.extra_data.get("pic640x480") pic_small_url = self.account.extra_data.get("pic190x190") if pic_big_url: return pic_big_url elif pic_medium_url: return pic_medium_url elif pic_small_url: return pic_small_url else: return ret class OdnoklassnikiProvider(OAuth2Provider): id = "odnoklassniki" name = "Odnoklassniki" account_class = OdnoklassnikiAccount oauth2_adapter_class = OdnoklassnikiOAuth2Adapter def extract_uid(self, data): return data["uid"] def extract_common_fields(self, data): return dict( last_name=data.get("last_name"), first_name=data.get("first_name"), email=data.get("email"), ) provider_classes = [OdnoklassnikiProvider] ================================================ FILE: allauth/socialaccount/providers/odnoklassniki/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.odnoklassniki.provider import OdnoklassnikiProvider urlpatterns = default_urlpatterns(OdnoklassnikiProvider) ================================================ FILE: allauth/socialaccount/providers/odnoklassniki/views.py ================================================ from hashlib import md5 from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) USER_FIELDS = [ "age", "birthday", "current_status", "current_status_date", "current_status_id", "email", "first_name", "gender", "has_email", "last_name", "locale", "location", "name", "online", "photo_id", "pic1024x768", # big "pic190x190", # small "pic640x480", # medium "pic_1", # aka pic50x50 "pic_2", # aka pic128max "uid", ] class OdnoklassnikiOAuth2Adapter(OAuth2Adapter): provider_id = "odnoklassniki" access_token_url = "https://api.odnoklassniki.ru/oauth/token.do" # nosec authorize_url = "https://www.odnoklassniki.ru/oauth/authorize" profile_url = "https://api.odnoklassniki.ru/fb.do" access_token_method = "POST" # nosec def complete_login(self, request, app, token, **kwargs): data = { "method": "users.getCurrentUser", "access_token": token.token, "fields": ",".join(USER_FIELDS), "format": "JSON", "application_key": app.key, } # Ondoklassniki prescribes a weak algo suffix = md5( f"{data['access_token']:s}{app.secret:s}".encode() ).hexdigest() # nosec check_list = sorted( [f"{k:s}={v:s}" for k, v in data.items() if k != "access_token"] ) data["sig"] = md5( ("".join(check_list) + suffix).encode("utf-8") ).hexdigest() # nosec with get_adapter().get_requests_session() as sess: response = sess.get(self.profile_url, params=data) extra_data = response.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(OdnoklassnikiOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(OdnoklassnikiOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/okta/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/okta/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.okta.views import OktaOAuth2Adapter class OktaAccount(ProviderAccount): pass class OktaProvider(OAuth2Provider): id = "okta" name = "Okta" account_class = OktaAccount oauth2_adapter_class = OktaOAuth2Adapter def get_default_scope(self): return ["openid", "profile", "email", "offline_access"] def extract_uid(self, data): uid_field = self.app.settings.get("uid_field", "sub") return str(data[uid_field]) def extract_extra_data(self, data): return data def extract_email_addresses(self, data): return [ EmailAddress( email=data["email"], verified=bool(data["email_verified"]), primary=True ) ] def extract_common_fields(self, data): ret = dict( email=data["email"], last_name=data["family_name"], first_name=data["given_name"], ) preferred_username = data.get("preferred_username") if preferred_username: ret["username"] = preferred_username.partition("@")[0] return ret provider_classes = [OktaProvider] ================================================ FILE: allauth/socialaccount/providers/okta/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.okta.provider import OktaProvider urlpatterns = default_urlpatterns(OktaProvider) ================================================ FILE: allauth/socialaccount/providers/okta/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class OktaOAuth2Adapter(OAuth2Adapter): provider_id = "okta" settings = app_settings.PROVIDERS.get(provider_id, {}) okta_base_url = settings.get("OKTA_BASE_URL") @property def access_token_url(self): return f"https://{self.okta_base_url}/oauth2/v1/token" @property def authorize_url(self): return f"https://{self.okta_base_url}/oauth2/v1/authorize" @property def userinfo_url(self): return f"https://{self.okta_base_url}/oauth2/v1/userinfo" @property def access_token_method(self): return "POST" def complete_login(self, request, app, token, **kwargs): """ Get the user info from userinfo endpoint and return a A populated instance of the `SocialLogin` model (unsaved) :param request: :param app: :param token: :param kwargs: :return: """ headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.userinfo_url, headers=headers) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(OktaOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(OktaOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/openid/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/openid/admin.py ================================================ from django.contrib import admin from .models import OpenIDNonce, OpenIDStore class OpenIDStoreAdmin(admin.ModelAdmin): pass class OpenIDNonceAdmin(admin.ModelAdmin): pass admin.site.register(OpenIDStore, OpenIDStoreAdmin) admin.site.register(OpenIDNonce, OpenIDNonceAdmin) ================================================ FILE: allauth/socialaccount/providers/openid/forms.py ================================================ from django import forms from django.utils.safestring import mark_safe class LoginForm(forms.Form): openid = forms.URLField( label=("OpenID"), help_text=mark_safe( 'Get an OpenID' ), # nosec ) next = forms.CharField(widget=forms.HiddenInput, required=False) process = forms.CharField(widget=forms.HiddenInput, required=False) ================================================ FILE: allauth/socialaccount/providers/openid/migrations/0001_initial.py ================================================ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [] operations = [ migrations.CreateModel( name="OpenIDNonce", fields=[ ( "id", models.AutoField( verbose_name="ID", serialize=False, auto_created=True, primary_key=True, ), ), ("server_url", models.CharField(max_length=255)), ("timestamp", models.IntegerField()), ("salt", models.CharField(max_length=255)), ("date_created", models.DateTimeField(auto_now_add=True)), ], options={}, bases=(models.Model,), ), migrations.CreateModel( name="OpenIDStore", fields=[ ( "id", models.AutoField( verbose_name="ID", serialize=False, auto_created=True, primary_key=True, ), ), ("server_url", models.CharField(max_length=255)), ("handle", models.CharField(max_length=255)), ("secret", models.TextField()), ("issued", models.IntegerField()), ("lifetime", models.IntegerField()), ("assoc_type", models.TextField()), ], options={}, bases=(models.Model,), ), ] ================================================ FILE: allauth/socialaccount/providers/openid/migrations/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/openid/models.py ================================================ from django.db import models class OpenIDStore(models.Model): server_url = models.CharField(max_length=255) handle = models.CharField(max_length=255) secret = models.TextField() issued = models.IntegerField() lifetime = models.IntegerField() assoc_type = models.TextField() def __str__(self) -> str: return self.server_url class OpenIDNonce(models.Model): server_url = models.CharField(max_length=255) timestamp = models.IntegerField() salt = models.CharField(max_length=255) date_created = models.DateTimeField(auto_now_add=True) def __str__(self) -> str: return self.server_url ================================================ FILE: allauth/socialaccount/providers/openid/provider.py ================================================ from urllib.parse import urlparse from django.urls import reverse from django.utils.http import urlencode from allauth.socialaccount.providers.base import Provider, ProviderAccount from .utils import ( AXAttribute, OldAXAttribute, SRegField, get_email_from_response, get_value_from_response, ) class OpenIDAccount(ProviderAccount): def get_brand(self): ret = super().get_brand() domain = urlparse(self.account.uid).netloc provider_map = {} for d, p in provider_map.items(): if domain.lower().find(d) >= 0: ret = p break return ret def to_str(self): return self.account.uid class OpenIDProvider(Provider): id = "openid" name = "OpenID" account_class = OpenIDAccount uses_apps = False def get_login_url(self, request, **kwargs): url = reverse("openid_login") if kwargs: url += f"?{urlencode(kwargs)}" return url def get_brands(self): default_servers = [] return self.get_settings().get("SERVERS", default_servers) def get_server_settings(self, endpoint): servers = self.get_settings().get("SERVERS", []) for server in servers: if endpoint is not None and endpoint.startswith(server.get("openid_url")): return server return {} def extract_extra_data(self, response): extra_data = {} server_settings = self.get_server_settings(response.endpoint.server_url) extra_attributes = server_settings.get("extra_attributes", []) for attribute_id, name, _ in extra_attributes: extra_data[attribute_id] = get_value_from_response( response, ax_names=[name] ) return extra_data def extract_uid(self, response): return response.identity_url def extract_common_fields(self, response): first_name = ( get_value_from_response( response, ax_names=[ AXAttribute.PERSON_FIRST_NAME, OldAXAttribute.PERSON_FIRST_NAME, ], ) or "" ) last_name = ( get_value_from_response( response, ax_names=[ AXAttribute.PERSON_LAST_NAME, OldAXAttribute.PERSON_LAST_NAME, ], ) or "" ) name = ( get_value_from_response( response, sreg_names=[SRegField.NAME], ax_names=[AXAttribute.PERSON_NAME, OldAXAttribute.PERSON_NAME], ) or "" ) return dict( email=get_email_from_response(response), first_name=first_name, last_name=last_name, name=name, ) provider_classes = [OpenIDProvider] ================================================ FILE: allauth/socialaccount/providers/openid/urls.py ================================================ from django.urls import path from . import views urlpatterns = [ path("openid/login/", views.login, name="openid_login"), path("openid/callback/", views.callback, name="openid_callback"), ] ================================================ FILE: allauth/socialaccount/providers/openid/utils.py ================================================ import base64 import pickle # nosec from collections import UserDict from openid.association import Association as OIDAssociation from openid.extensions.ax import FetchResponse from openid.extensions.sreg import SRegResponse from openid.store.interface import OpenIDStore as OIDStore from allauth.account.internal.emailkit import valid_email_or_none from .models import OpenIDNonce, OpenIDStore class JSONSafeSession(UserDict): """ openid puts e.g. class OpenIDServiceEndpoint in the session. Django 1.6 no longer pickles stuff, so we'll need to do some hacking here... """ def __init__(self, session): UserDict.__init__(self) self.data = session def __setitem__(self, key, value): data = base64.b64encode(pickle.dumps(value)).decode("ascii") return UserDict.__setitem__(self, key, data) def __getitem__(self, key): data = UserDict.__getitem__(self, key) # We can avoid all of this once this is released: # https://github.com/necaris/python3-openid/pull/71 # We're not loading pickled data from an external source, so this # is safe. return pickle.loads(base64.b64decode(data.encode("ascii"))) # nosec class OldAXAttribute: PERSON_NAME = "http://openid.net/schema/namePerson" PERSON_FIRST_NAME = "http://openid.net/schema/namePerson/first" PERSON_LAST_NAME = "http://openid.net/schema/namePerson/last" class AXAttribute: CONTACT_EMAIL = "http://axschema.org/contact/email" PERSON_NAME = "http://axschema.org/namePerson" PERSON_FIRST_NAME = "http://axschema.org/namePerson/first" PERSON_LAST_NAME = "http://axschema.org/namePerson/last" AXAttributes = [ AXAttribute.CONTACT_EMAIL, AXAttribute.PERSON_NAME, AXAttribute.PERSON_FIRST_NAME, AXAttribute.PERSON_LAST_NAME, OldAXAttribute.PERSON_NAME, OldAXAttribute.PERSON_FIRST_NAME, OldAXAttribute.PERSON_LAST_NAME, ] class SRegField: EMAIL = "email" NAME = "fullname" SRegFields = [ SRegField.EMAIL, SRegField.NAME, ] class DBOpenIDStore(OIDStore): max_nonce_age = 6 * 60 * 60 def storeAssociation(self, server_url, assoc=None): try: secret = base64.encodebytes(assoc.secret) except AttributeError: # Python 2.x compat secret = base64.encodestring(assoc.secret) else: secret = secret.decode() OpenIDStore.objects.create( server_url=server_url, handle=assoc.handle, secret=secret, issued=assoc.issued, lifetime=assoc.lifetime, assoc_type=assoc.assoc_type, ) def getAssociation(self, server_url, handle=None): stored_assocs = OpenIDStore.objects.filter(server_url=server_url) if handle: stored_assocs = stored_assocs.filter(handle=handle) stored_assocs.order_by("-issued") if not stored_assocs.exists(): return None return_val = None for stored_assoc in stored_assocs: assoc = OIDAssociation( stored_assoc.handle, base64.decodebytes(stored_assoc.secret.encode("utf-8")), stored_assoc.issued, stored_assoc.lifetime, stored_assoc.assoc_type, ) # See: # necaris/python3-openid@1abb155c8fc7b508241cbe9d2cae24f18e4a379b if hasattr(assoc, "getExpiresIn"): expires_in = assoc.getExpiresIn() else: expires_in = assoc.expiresIn if expires_in == 0: stored_assoc.delete() else: if return_val is None: return_val = assoc return return_val def removeAssociation(self, server_url, handle): stored_assocs = OpenIDStore.objects.filter(server_url=server_url) if handle: stored_assocs = stored_assocs.filter(handle=handle) stored_assocs.delete() def useNonce(self, server_url, timestamp, salt): try: OpenIDNonce.objects.get( server_url=server_url, timestamp=timestamp, salt=salt ) except OpenIDNonce.DoesNotExist: OpenIDNonce.objects.create( server_url=server_url, timestamp=timestamp, salt=salt ) return True return False def get_email_from_response(response): email = None sreg = SRegResponse.fromSuccessResponse(response) if sreg: email = valid_email_or_none(sreg.get(SRegField.EMAIL)) if not email: ax = FetchResponse.fromSuccessResponse(response) if ax: try: values = ax.get(AXAttribute.CONTACT_EMAIL) if values: email = valid_email_or_none(values[0]) except KeyError: pass return email def get_value_from_response(response, sreg_names=None, ax_names=None): value = None if sreg_names: sreg = SRegResponse.fromSuccessResponse(response) if sreg: for name in sreg_names: value = sreg.get(name) if value: break if not value and ax_names: ax = FetchResponse.fromSuccessResponse(response) if ax: for name in ax_names: try: values = ax.get(name) if values: value = values[0] except KeyError: pass if value: break return value ================================================ FILE: allauth/socialaccount/providers/openid/views.py ================================================ from django.contrib.auth import REDIRECT_FIELD_NAME from django.http import HttpResponse, HttpResponseBase, HttpResponseRedirect from django.shortcuts import render from django.urls import reverse from django.utils.decorators import method_decorator from django.views import View from django.views.decorators.csrf import csrf_exempt from openid.consumer import consumer from openid.consumer.discover import DiscoveryFailure from openid.extensions.ax import AttrInfo, FetchRequest from openid.extensions.sreg import SRegRequest from openid.message import InvalidOpenIDNamespace from allauth.account.internal.decorators import login_not_required from allauth.socialaccount.app_settings import QUERY_EMAIL from allauth.socialaccount.helpers import ( complete_social_login, render_authentication_error, ) from allauth.socialaccount.models import SocialLogin from allauth.socialaccount.providers.openid.provider import OpenIDProvider from ..base import AuthError from .forms import LoginForm from .utils import AXAttributes, DBOpenIDStore, JSONSafeSession, SRegFields def _openid_consumer(request, provider, endpoint): server_settings = provider.get_server_settings(endpoint) stateless = server_settings.get("stateless", False) store = None if stateless else DBOpenIDStore() client = consumer.Consumer(JSONSafeSession(request.session), store) return client @method_decorator(login_not_required, name="dispatch") class OpenIDLoginView(View): template_name = "openid/login.html" form_class = LoginForm provider_class = OpenIDProvider def dispatch(self, request, *args, **kwargs) -> HttpResponseBase: self.provider = self.provider_class(request) return super().dispatch(request, *args, **kwargs) def get(self, request) -> HttpResponse: form = self.get_form() if not form.is_valid(): return render(request, self.template_name, {"form": form}) try: return self.perform_openid_auth(form) except (UnicodeDecodeError, DiscoveryFailure) as e: # UnicodeDecodeError: necaris/python3-openid#1 return render_authentication_error(request, self.provider, exception=e) def post(self, request) -> HttpResponse: form = self.get_form() if form.is_valid(): try: return self.perform_openid_auth(form) except (UnicodeDecodeError, DiscoveryFailure) as e: form._errors["openid"] = form.error_class([e]) return render(request, self.template_name, {"form": form}) def get_form(self): if self.request.method == "GET" and "openid" not in self.request.GET: return self.form_class( initial={ "next": self.request.GET.get(REDIRECT_FIELD_NAME), "process": self.request.GET.get("process"), } ) return self.form_class( dict(list(self.request.GET.items()) + list(self.request.POST.items())) ) def get_client(self, provider, endpoint): return _openid_consumer(self.request, provider, endpoint) def get_realm(self, provider): return provider.get_settings().get( "REALM", self.request.build_absolute_uri("/") ) def get_callback_url(self): return reverse(callback) def perform_openid_auth(self, form): if not form.is_valid(): return form request = self.request provider = self.provider endpoint = form.cleaned_data["openid"] client = self.get_client(provider, endpoint) realm = self.get_realm(provider) auth_request = client.begin(endpoint) if QUERY_EMAIL: sreg = SRegRequest() for name in SRegFields: sreg.requestField(field_name=name, required=True) auth_request.addExtension(sreg) ax = FetchRequest() for name in AXAttributes: ax.add(AttrInfo(name, required=True)) server_settings = provider.get_server_settings(request.GET.get("openid")) extra_attributes = server_settings.get("extra_attributes", []) for _, name, required in extra_attributes: ax.add(AttrInfo(name, required=required)) auth_request.addExtension(ax) SocialLogin.stash_state(request) # Fix for issues 1523 and 2072 (github django-allauth) if "next" in form.cleaned_data and form.cleaned_data["next"]: auth_request.return_to_args["next"] = form.cleaned_data["next"] redirect_url = auth_request.redirectURL( realm, request.build_absolute_uri(self.get_callback_url()) ) return HttpResponseRedirect(redirect_url) login = OpenIDLoginView.as_view() @method_decorator(login_not_required, name="dispatch") class OpenIDCallbackView(View): provider_class = OpenIDProvider def get(self, request) -> HttpResponse: provider = self.provider = self.provider_class(request) endpoint = request.GET.get("openid.op_endpoint", "") client = self.get_client(provider, endpoint) try: response = self.get_openid_response(client) except InvalidOpenIDNamespace as e: return render_authentication_error(self.request, self.provider, exception=e) if response.status == consumer.SUCCESS: login = provider.sociallogin_from_response(request, response) login.state = SocialLogin.unstash_state(request) return self.complete_login(login) else: if response.status == consumer.CANCEL: error = AuthError.CANCELLED else: error = AuthError.UNKNOWN return self.render_error(error) post = get def complete_login(self, login) -> HttpResponse: return complete_social_login(self.request, login) def render_error(self, error) -> HttpResponse: return render_authentication_error(self.request, self.provider, error=error) def get_client(self, provider, endpoint): return _openid_consumer(self.request, provider, endpoint) def get_openid_response(self, client): return client.complete( dict(list(self.request.GET.items()) + list(self.request.POST.items())), self.request.build_absolute_uri(self.request.path), ) callback = csrf_exempt(OpenIDCallbackView.as_view()) ================================================ FILE: allauth/socialaccount/providers/openid_connect/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/openid_connect/provider.py ================================================ import requests from django.urls import reverse from django.utils.http import urlencode from allauth.account.models import EmailAddress from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.internal import jwtkit from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.client import OAuth2Error from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.openid_connect.views import ( OpenIDConnectOAuth2Adapter, ) def _pick_data(data: dict) -> dict: # Prefer userinfo, it likely has more info compared to the ID token. if userinfo := data.get("userinfo"): return userinfo elif id_token := data.get("id_token"): return id_token # For backwards compatibility with <65.11 return data class OpenIDConnectProviderAccount(ProviderAccount): def get_user_data(self) -> dict | None: return _pick_data(self.account.extra_data) class OpenIDConnectProvider(OAuth2Provider): id = "openid_connect" name = "OpenID Connect" account_class = OpenIDConnectProviderAccount oauth2_adapter_class = OpenIDConnectOAuth2Adapter supports_token_authentication = True def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.name = self.app.name @property def server_url(self): url = self.app.settings["server_url"] return self.wk_server_url(url) def wk_server_url(self, url): well_known_uri = "/.well-known/openid-configuration" if "/.well-known/" not in url: url += well_known_uri return url def get_login_url(self, request, **kwargs): url = reverse( f"{self.app.provider}_login", kwargs={"provider_id": self.app.provider_id} ) if kwargs: url = f"{url}?{urlencode(kwargs)}" return url def get_callback_url(self): return reverse( f"{self.app.provider}_callback", kwargs={"provider_id": self.app.provider_id}, ) @property def token_auth_method(self): return self.app.settings.get("token_auth_method") def get_default_scope(self): return ["openid", "profile", "email"] def extract_uid(self, data): data = _pick_data(data) return str(data[self.app.settings.get("uid_field", "sub")]) def extract_common_fields(self, data): data = _pick_data(data) return dict( email=data.get("email"), username=data.get("preferred_username"), name=data.get("name"), user_id=data.get("user_id"), picture=data.get("picture"), last_name=data.get("family_name"), first_name=data.get("given_name"), ) def extract_email_addresses(self, data): data = _pick_data(data) addresses = [] email = data.get("email") if email: addresses.append( EmailAddress( email=email, verified=data.get("email_verified", False), primary=True, ) ) return addresses def get_oauth2_adapter(self, request): return self.oauth2_adapter_class(request, self.app.provider_id) def verify_token(self, request, token): id_token = token.get("id_token") if not id_token: raise get_adapter().validation_error("invalid_token") try: oauth2_adapter = self.get_oauth2_adapter(request) openid_config = oauth2_adapter.openid_config identity_data = jwtkit.verify_and_decode( credential=id_token, keys_url=openid_config["jwks_uri"], issuer=openid_config["issuer"], audience=[self.app.client_id], lookup_kid=jwtkit.lookup_kid_jwk, ) except (OAuth2Error, requests.RequestException) as e: raise get_adapter().validation_error("invalid_token") from e login = self.sociallogin_from_response(request, identity_data) return login provider_classes = [OpenIDConnectProvider] ================================================ FILE: allauth/socialaccount/providers/openid_connect/urls.py ================================================ from django.urls import include, path, re_path from allauth.socialaccount import app_settings from . import views urlpatterns = [ re_path( r"^(?P[^/]+)/", include( [ path( "login/", views.login, name="openid_connect_login", ), path( "login/callback/", views.callback, name="openid_connect_callback", ), ] ), ) ] if app_settings.OPENID_CONNECT_URL_PREFIX: urlpatterns = [ path(f"{app_settings.OPENID_CONNECT_URL_PREFIX}/", include(urlpatterns)) ] ================================================ FILE: allauth/socialaccount/providers/openid_connect/views.py ================================================ from django.http import Http404 from django.urls import reverse from allauth.account.internal.decorators import login_not_required from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.internal import jwtkit from allauth.socialaccount.models import SocialApp, SocialToken from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) from allauth.utils import build_absolute_uri class OpenIDConnectOAuth2Adapter(OAuth2Adapter): def __init__(self, request, provider_id): self.provider_id = provider_id super().__init__(request) @property def openid_config(self): if not hasattr(self, "_openid_config"): server_url = self.get_provider().server_url with get_adapter().get_requests_session() as sess: resp = sess.get(server_url) resp.raise_for_status() self._openid_config = resp.json() return self._openid_config @property def basic_auth(self): token_auth_method = self.get_provider().app.settings.get("token_auth_method") if token_auth_method: return token_auth_method == "client_secret_basic" # nosec methods = self.openid_config.get("token_endpoint_auth_methods_supported", []) # Basic auth is problematic, especially when client ID contains a colon. return "client_secret_post" not in methods and "client_secret_basic" in methods @property def access_token_url(self): return self.openid_config["token_endpoint"] @property def authorize_url(self): return self.openid_config["authorization_endpoint"] @property def profile_url(self): return self.openid_config["userinfo_endpoint"] def complete_login(self, request, app, token: SocialToken, **kwargs): id_token_str = kwargs["response"].get("id_token") fetch_userinfo = app.settings.get("fetch_userinfo", True) data = {} if fetch_userinfo or (not id_token_str): data["userinfo"] = self._fetch_user_info(token.token) if id_token_str: data["id_token"] = self._decode_id_token(app, id_token_str) return self.get_provider().sociallogin_from_response(request, data) def _fetch_user_info(self, access_token: str) -> dict: headers = {"Authorization": f"Bearer {access_token}"} with get_adapter().get_requests_session() as sess: response = sess.get(self.profile_url, headers=headers) response.raise_for_status() return response.json() def _decode_id_token(self, app: SocialApp, id_token: str) -> dict: """ If the token was received by direct communication protected by TLS between this library and Google, we are allowed to skip checking the token signature according to the OpenID Connect Core 1.0 specification. https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation """ verify_signature = not self.did_fetch_access_token return jwtkit.verify_and_decode( credential=id_token, keys_url=self.openid_config["jwks_uri"], issuer=self.openid_config["issuer"], audience=app.client_id, lookup_kid=jwtkit.lookup_kid_jwk, verify_signature=verify_signature, ) def get_callback_url(self, request, app): callback_url = reverse( "openid_connect_callback", kwargs={"provider_id": self.provider_id} ) protocol = self.redirect_uri_protocol return build_absolute_uri(request, callback_url, protocol) @login_not_required def login(request, provider_id): try: view = OAuth2LoginView.adapter_view( OpenIDConnectOAuth2Adapter(request, provider_id) ) return view(request) except SocialApp.DoesNotExist: raise Http404 @login_not_required def callback(request, provider_id): try: view = OAuth2CallbackView.adapter_view( OpenIDConnectOAuth2Adapter(request, provider_id) ) return view(request) except SocialApp.DoesNotExist: raise Http404 ================================================ FILE: allauth/socialaccount/providers/openstreetmap/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/openstreetmap/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth.provider import OAuthProvider from allauth.socialaccount.providers.openstreetmap.views import ( OpenStreetMapOAuthAdapter, ) class OpenStreetMapAccount(ProviderAccount): def get_profile_url(self): display_name = self.account.extra_data["display_name"] return f"https://www.openstreetmap.org/user/{display_name}" def get_avatar_url(self): ret = None if img := self.account.extra_data.get("img"): ret = img.get("href") if not ret: # Backwards compatible (OSM provider data originating from XML) ret = self.account.extra_data.get("avatar") return ret def get_username(self): return self.account.extra_data["display_name"] class OpenStreetMapProvider(OAuthProvider): id = "openstreetmap" name = "OpenStreetMap" account_class = OpenStreetMapAccount oauth_adapter_class = OpenStreetMapOAuthAdapter def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict(username=data["display_name"]) provider_classes = [OpenStreetMapProvider] ================================================ FILE: allauth/socialaccount/providers/openstreetmap/urls.py ================================================ from allauth.socialaccount.providers.oauth.urls import default_urlpatterns from allauth.socialaccount.providers.openstreetmap.provider import OpenStreetMapProvider urlpatterns = default_urlpatterns(OpenStreetMapProvider) ================================================ FILE: allauth/socialaccount/providers/openstreetmap/views.py ================================================ from allauth.socialaccount.providers.oauth.client import OAuth from allauth.socialaccount.providers.oauth.views import ( OAuthAdapter, OAuthCallbackView, OAuthLoginView, ) class OpenStreetMapAPI(OAuth): url = "https://api.openstreetmap.org/api/0.6/user/details.json" def get_user_info(self): data = self.query(self.url).json() return data["user"] class OpenStreetMapOAuthAdapter(OAuthAdapter): provider_id = "openstreetmap" request_token_url = "https://www.openstreetmap.org/oauth/request_token" # nosec access_token_url = "https://www.openstreetmap.org/oauth/access_token" # nosec authorize_url = "https://www.openstreetmap.org/oauth/authorize" def complete_login(self, request, app, token, response): client = OpenStreetMapAPI( request, app.client_id, app.secret, self.request_token_url ) extra_data = client.get_user_info() return self.get_provider().sociallogin_from_response(request, extra_data) oauth_login = OAuthLoginView.adapter_view(OpenStreetMapOAuthAdapter) oauth_callback = OAuthCallbackView.adapter_view(OpenStreetMapOAuthAdapter) ================================================ FILE: allauth/socialaccount/providers/orcid/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/orcid/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.orcid.views import OrcidOAuth2Adapter class Scope: USERINFO_PROFILE = "/authenticate" class OrcidAccount(ProviderAccount): def get_profile_url(self): return extract_from_dict(self.account.extra_data, ["orcid-identifier", "uri"]) class OrcidProvider(OAuth2Provider): id = "orcid" name = "Orcid.org" account_class = OrcidAccount oauth2_adapter_class = OrcidOAuth2Adapter def get_default_scope(self): return [Scope.USERINFO_PROFILE] def extract_uid(self, data): return extract_from_dict(data, ["orcid-identifier", "path"]) def extract_common_fields(self, data): common_fields = dict( email=extract_from_dict(data, ["person", "emails", "email", 0, "email"]), last_name=extract_from_dict( data, ["person", "name", "family-name", "value"] ), first_name=extract_from_dict( data, ["person", "name", "given-names", "value"] ), ) return {key: value for (key, value) in common_fields.items() if value} provider_classes = [OrcidProvider] def extract_from_dict(data, path): """ Navigate `data`, a multidimensional array (list or dictionary), and returns the object at `path`. """ value = data try: for key in path: value = value[key] return value except (KeyError, IndexError, TypeError): return "" ================================================ FILE: allauth/socialaccount/providers/orcid/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.orcid.provider import OrcidProvider urlpatterns = default_urlpatterns(OrcidProvider) ================================================ FILE: allauth/socialaccount/providers/orcid/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class OrcidOAuth2Adapter(OAuth2Adapter): provider_id = "orcid" member_api_default = False base_domain_default = "orcid.org" settings = app_settings.PROVIDERS.get(provider_id, {}) base_domain = settings.get("BASE_DOMAIN", base_domain_default) member_api = settings.get("MEMBER_API", member_api_default) api_domain = f"{'api' if member_api else 'pub'}.{base_domain}" authorize_url = f"https://{base_domain}/oauth/authorize" access_token_url = f"https://{api_domain}/oauth/token" profile_url = f"https://{api_domain}/v3.0/%s/record" def complete_login(self, request, app, token, **kwargs): params = {} if self.member_api: params["access_token"] = token.token headers = {"accept": "application/orcid+json"} with get_adapter().get_requests_session() as sess: url = self.profile_url % kwargs["response"]["orcid"] resp = sess.get(url, params=params, headers=headers) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(OrcidOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(OrcidOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/patreon/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/patreon/constants.py ================================================ from django.conf import settings PROVIDER_ID = "patreon" API_VERSION = ( getattr(settings, "SOCIALACCOUNT_PROVIDERS", {}) .get("patreon", {}) .get("VERSION", "v1") ) USE_API_V2 = True if API_VERSION == "v2" else False API_URL = f"https://www.patreon.com/api/oauth2/{API_VERSION if USE_API_V2 else 'api'}" ================================================ FILE: allauth/socialaccount/providers/patreon/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/patreon/provider.py ================================================ """ Provider for Patreon """ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.patreon.views import PatreonOAuth2Adapter from .constants import PROVIDER_ID, USE_API_V2 class PatreonAccount(ProviderAccount): def get_avatar_url(self): return self.account.extra_data.get("attributes").get("thumb_url") def to_str(self): email = self.account.extra_data.get("attributes", {}).get("email") return email or super().to_str() class PatreonProvider(OAuth2Provider): id = PROVIDER_ID name = "Patreon" account_class = PatreonAccount oauth2_adapter_class = PatreonOAuth2Adapter def get_default_scope(self): if USE_API_V2: return [ "identity", "identity[email]", "campaigns", "campaigns.members", ] return ["pledges-to-me", "users", "my-campaign"] def extract_uid(self, data): return data.get("id") def extract_common_fields(self, data): details = data["attributes"] return { "email": details.get("email"), "fullname": details.get("full_name"), "first_name": details.get("first_name"), "last_name": details.get("last_name"), } provider_classes = [PatreonProvider] ================================================ FILE: allauth/socialaccount/providers/patreon/urls.py ================================================ """URLs for Patreon Provider""" from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.patreon.provider import PatreonProvider urlpatterns = default_urlpatterns(PatreonProvider) ================================================ FILE: allauth/socialaccount/providers/patreon/views.py ================================================ """ Views for PatreonProvider https://www.patreon.com/platform/documentation/oauth """ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) from .constants import API_URL, PROVIDER_ID, USE_API_V2 class PatreonOAuth2Adapter(OAuth2Adapter): provider_id = PROVIDER_ID access_token_url = "https://www.patreon.com/api/oauth2/token" # nosec authorize_url = "https://www.patreon.com/oauth2/authorize" profile_url = "{}/{}".format( API_URL, ( "identity?include=memberships&fields%5Buser%5D=email,first_name," "full_name,image_url,last_name,social_connections," "thumb_url,url,vanity" if USE_API_V2 else "current_user" ), ) def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) extra_data = resp.json().get("data") if USE_API_V2: # Extract tier/pledge level for Patreon API v2: try: memberships = extra_data["relationships"]["memberships"] member_id = memberships["data"][0]["id"] member_url = ( "{}/members/{}?include=" "currently_entitled_tiers&fields%5Btier%5D=title" ).format(API_URL, member_id) resp_member = sess.get(member_url, headers=headers) resp_data = resp_member.json() pledge_title = resp_data["included"][0]["attributes"]["title"] extra_data["pledge_level"] = pledge_title except (KeyError, IndexError): extra_data["pledge_level"] = None pass return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(PatreonOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(PatreonOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/paypal/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/paypal/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.paypal.views import PaypalOAuth2Adapter class PaypalAccount(ProviderAccount): def get_avatar_url(self): return self.account.extra_data.get("picture") class PaypalProvider(OAuth2Provider): id = "paypal" name = "Paypal" account_class = PaypalAccount oauth2_adapter_class = PaypalOAuth2Adapter def get_default_scope(self): # See: https://developer.paypal.com/docs/integration/direct/identity/attributes/ # noqa return ["openid", "email"] def extract_uid(self, data): return str(data["user_id"]) def extract_common_fields(self, data): # See: https://developer.paypal.com/docs/api/#get-user-information return dict( first_name=data.get("given_name", ""), last_name=data.get("family_name", ""), email=data.get("email"), ) provider_classes = [PaypalProvider] ================================================ FILE: allauth/socialaccount/providers/paypal/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.paypal.provider import PaypalProvider urlpatterns = default_urlpatterns(PaypalProvider) ================================================ FILE: allauth/socialaccount/providers/paypal/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class PaypalOAuth2Adapter(OAuth2Adapter): provider_id = "paypal" @property def authorize_url(self): path = "webapps/auth/protocol/openidconnect/v1/authorize" return f"https://www.{self._get_endpoint()}/{path}" @property def access_token_url(self): path = "v1/identity/openidconnect/tokenservice" return f"https://api.{self._get_endpoint()}/{path}" @property def profile_url(self): path = "v1/identity/openidconnect/userinfo" return f"https://api.{self._get_endpoint()}/{path}" def _get_endpoint(self): settings = self.get_provider().get_settings() if settings.get("MODE") == "live": return "paypal.com" else: return "sandbox.paypal.com" def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: response = sess.post( self.profile_url, params={"schema": "openid", "access_token": token.token}, ) extra_data = response.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(PaypalOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(PaypalOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/pinterest/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/pinterest/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.pinterest.views import PinterestOAuth2Adapter class PinterestAccount(ProviderAccount): def get_username(self): return self.account.extra_data.get("username") def get_profile_url(self): # v5 extra_data not same as v1 username = self.get_username() if username: return f"https://www.pinterest.com/{username}/" return self.account.extra_data.get("url") def get_avatar_url(self): return self.account.extra_data.get("profile_image") class PinterestProvider(OAuth2Provider): id = "pinterest" name = "Pinterest" account_class = PinterestAccount oauth2_adapter_class = PinterestOAuth2Adapter @property def api_version(self): return self.get_settings().get("API_VERSION", "v1") def get_default_scope(self): # See: https://developers.pinterest.com/docs/api/overview/#scopes if self.api_version == "v5": # See: https://developers.pinterest.com/docs/getting-started/scopes/ return ["user_accounts:read"] elif self.api_version == "v3": return ["read_users"] return ["read_public"] def extract_extra_data(self, data): if self.api_version == "v5": return data return data.get("data", {}) def extract_uid(self, data): if self.api_version == "v5": return data["username"] return str(data["data"]["id"]) def extract_common_fields(self, data): if self.api_version == "v5": return dict(username=data["username"]) return dict( first_name=data.get("data", {}).get("first_name"), last_name=data.get("data", {}).get("last_name"), ) provider_classes = [PinterestProvider] ================================================ FILE: allauth/socialaccount/providers/pinterest/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.pinterest.provider import PinterestProvider urlpatterns = default_urlpatterns(PinterestProvider) ================================================ FILE: allauth/socialaccount/providers/pinterest/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class PinterestOAuth2Adapter(OAuth2Adapter): provider_id = "pinterest" provider_default_url = "api.pinterest.com" provider_default_api_version = "v1" settings = app_settings.PROVIDERS.get(provider_id, {}) provider_base_url = settings.get("PINTEREST_URL", provider_default_url) provider_api_version = settings.get("API_VERSION", provider_default_api_version) authorize_url = "https://www.pinterest.com/oauth/" access_token_url = f"https://{provider_base_url}/{provider_api_version}/oauth/token" basic_auth = True if provider_api_version == "v5": profile_url = f"https://{provider_base_url}/{provider_api_version}/user_account" elif provider_api_version == "v3": profile_url = f"https://{provider_base_url}/{provider_api_version}/users/me" else: profile_url = f"https://{provider_base_url}/{provider_api_version}/me" if provider_api_version == "v3": access_token_method = "PUT" # nosec def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: response = sess.get(self.profile_url, headers=headers) extra_data = response.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(PinterestOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(PinterestOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/pocket/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/pocket/client.py ================================================ from http import HTTPStatus from urllib.parse import urlencode from django.http import HttpResponseRedirect from django.utils.translation import gettext as _ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth.client import ( OAuthClient, OAuthError, get_token_prefix, ) from allauth.utils import build_absolute_uri class PocketOAuthClient(OAuthClient): def _get_request_token(self): """ Obtain a temporary request token to authorize an access token and to sign the request to obtain the access token """ if self.request_token is None: redirect_url = build_absolute_uri(self.request, self.callback_url) headers = { "X-Accept": "application/json", } data = { "consumer_key": self.consumer_key, "redirect_uri": redirect_url, } with get_adapter().get_requests_session() as sess: response = sess.post( url=self.request_token_url, json=data, headers=headers, ) if response.status_code != HTTPStatus.OK: raise OAuthError( _('Invalid response while obtaining request token from "%s".') % get_token_prefix(self.request_token_url) ) self.request_token = response.json()["code"] self.request.session[ f"oauth_{get_token_prefix(self.request_token_url)}_request_token" ] = self.request_token return self.request_token def get_redirect(self, authorization_url, extra_params): """ Returns a ``HttpResponseRedirect`` object to redirect the user to the Pocket authorization URL. """ request_token = self._get_request_token() params = { "request_token": request_token, "redirect_uri": self.request.build_absolute_uri(self.callback_url), } params.update(extra_params) url = f"{authorization_url}?{urlencode(params)}" return HttpResponseRedirect(url) def get_access_token(self): """ Obtain the access token to access private resources at the API endpoint. """ if self.access_token is None: request_token = self._get_rt_from_session() url = self.access_token_url headers = { "X-Accept": "application/json", } data = { "consumer_key": self.consumer_key, "code": request_token, } with get_adapter().get_requests_session() as sess: response = sess.post(url=url, headers=headers, json=data) if response.status_code != HTTPStatus.OK: raise OAuthError( _('Invalid response while obtaining access token from "%s".') % get_token_prefix(self.request_token_url) ) r = response.json() self.access_token = { "oauth_token": request_token, "oauth_token_secret": r["access_token"], "username": r["username"], } self.request.session[ f"oauth_{get_token_prefix(self.request_token_url)}_access_token" ] = self.access_token return self.access_token ================================================ FILE: allauth/socialaccount/providers/pocket/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/pocket/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth.provider import OAuthProvider from allauth.socialaccount.providers.pocket.views import PocketOAuthAdapter class PocketAccount(ProviderAccount): pass class PocketProvider(OAuthProvider): id = "pocket" name = "Pocket" account_class = PocketAccount oauth_adapter_class = PocketOAuthAdapter def extract_uid(self, data): return data["username"] def extract_common_fields(self, data): return dict( email=data["username"], ) def extract_email_addresses(self, data): return [ EmailAddress( email=data["username"], verified=True, primary=True, ) ] provider_classes = [PocketProvider] ================================================ FILE: allauth/socialaccount/providers/pocket/urls.py ================================================ from allauth.socialaccount.providers.oauth.urls import default_urlpatterns from allauth.socialaccount.providers.pocket.provider import PocketProvider urlpatterns = default_urlpatterns(PocketProvider) ================================================ FILE: allauth/socialaccount/providers/pocket/views.py ================================================ from ..oauth.views import OAuthAdapter, OAuthCallbackView, OAuthLoginView from .client import PocketOAuthClient class PocketOAuthAdapter(OAuthAdapter): provider_id = "pocket" request_token_url = "https://getpocket.com/v3/oauth/request" # nosec access_token_url = "https://getpocket.com/v3/oauth/authorize" # nosec authorize_url = "https://getpocket.com/auth/authorize" client_class = PocketOAuthClient def complete_login(self, request, app, token, response): return self.get_provider().sociallogin_from_response(request, response) oauth_login = OAuthLoginView.adapter_view(PocketOAuthAdapter) oauth_callback = OAuthCallbackView.adapter_view(PocketOAuthAdapter) ================================================ FILE: allauth/socialaccount/providers/questrade/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/questrade/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.questrade.views import QuestradeOAuth2Adapter class QuestradeAccount(ProviderAccount): pass class QuestradeProvider(OAuth2Provider): id = "questrade" name = "Questrade" account_class = QuestradeAccount oauth2_adapter_class = QuestradeOAuth2Adapter def extract_uid(self, data): return str(data["userId"]) provider_classes = [QuestradeProvider] ================================================ FILE: allauth/socialaccount/providers/questrade/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.questrade.provider import QuestradeProvider urlpatterns = default_urlpatterns(QuestradeProvider) ================================================ FILE: allauth/socialaccount/providers/questrade/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class QuestradeOAuth2Adapter(OAuth2Adapter): provider_id = "questrade" access_token_url = "https://login.questrade.com/oauth2/token" # nosec authorize_url = "https://login.questrade.com/oauth2/authorize" supports_state = False def complete_login(self, request, app, token, **kwargs): api_server = kwargs.get("response", {}).get( "api_server", "https://api01.iq.questrade.com/" ) headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(f"{api_server}v1/accounts", headers=headers) resp.raise_for_status() data = resp.json() data.update(kwargs) return self.get_provider().sociallogin_from_response(request, data) oauth2_login = OAuth2LoginView.adapter_view(QuestradeOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(QuestradeOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/quickbooks/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/quickbooks/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/quickbooks/provider.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.providers.base import ProviderAccount, ProviderException from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.quickbooks.views import QuickBooksOAuth2Adapter class QuickBooksAccount(ProviderAccount): pass class QuickBooksOAuth2Provider(OAuth2Provider): id = "quickbooks" # Name is displayed to ordinary users -- don't include protocol name = "QuickBooks" account_class = QuickBooksAccount oauth2_adapter_class = QuickBooksOAuth2Adapter def extract_uid(self, data): if "sub" not in data: raise ProviderException("QBO error", data) return str(data["sub"]) def get_profile_fields(self): default_fields = [ "address", "sub", "phoneNumber", "givenName", "familyName", "email", "emailVerified", ] fields = self.get_settings().get("PROFILE_FIELDS", default_fields) return fields def get_default_scope(self): scope = [ "openid", "com.intuit.quickbooks.accounting", "profile", "phone", ] if app_settings.QUERY_EMAIL: scope.append("email") return scope def extract_common_fields(self, data): return dict( email=data.get("email"), address=data.get("address"), sub=data.get("sub"), givenName=data.get("givenName"), familynName=data.get("familyName"), emailVerified=data.get("emailVerified"), phoneNumber=data.get("phoneNumber"), ) provider_classes = [QuickBooksOAuth2Provider] ================================================ FILE: allauth/socialaccount/providers/quickbooks/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.quickbooks.provider import QuickBooksOAuth2Provider urlpatterns = default_urlpatterns(QuickBooksOAuth2Provider) ================================================ FILE: allauth/socialaccount/providers/quickbooks/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class QuickBooksOAuth2Adapter(OAuth2Adapter): provider_id = "quickbooks" access_token_url = ( "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer" # nosec ) authorize_url = "https://appcenter.intuit.com/connect/oauth2" profile_test = "https://sandbox-accounts.platform.intuit.com/v1/openid_connect/userinfo" # NOQA profile_url = "https://accounts.platform.intuit.com/v1/openid_connect/userinfo" profile_url_method = "GET" access_token_method = "POST" # nosec def complete_login(self, request, app, token, **kwargs): realm_id = request.GET.get("realmId") extra_data = self.get_user_info(token) if realm_id: extra_data["realmId"] = realm_id return self.get_provider().sociallogin_from_response(request, extra_data) def get_user_info(self, token): auth_header = f"Bearer {token.token}" headers = { "Accept": "application/json", "Authorization": auth_header, "accept": "application/json", } is_sandbox = self.get_provider().get_settings().get("SANDBOX", False) url = self.profile_test if is_sandbox else self.profile_url with get_adapter().get_requests_session() as sess: resp = sess.get(url, headers=headers) resp.raise_for_status() return resp.json() oauth2_login = OAuth2LoginView.adapter_view(QuickBooksOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(QuickBooksOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/reddit/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/reddit/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.reddit.views import RedditAdapter class RedditAccount(ProviderAccount): pass class RedditProvider(OAuth2Provider): id = "reddit" name = "Reddit" account_class = RedditAccount oauth2_adapter_class = RedditAdapter def extract_uid(self, data): return data["name"] def extract_common_fields(self, data): return dict(username=data.get("name")) def get_default_scope(self): scope = ["identity"] return scope provider_classes = [RedditProvider] ================================================ FILE: allauth/socialaccount/providers/reddit/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.reddit.provider import RedditProvider urlpatterns = default_urlpatterns(RedditProvider) ================================================ FILE: allauth/socialaccount/providers/reddit/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class RedditAdapter(OAuth2Adapter): provider_id = "reddit" access_token_url = "https://www.reddit.com/api/v1/access_token" # nosec authorize_url = "https://www.reddit.com/api/v1/authorize" profile_url = "https://oauth.reddit.com/api/v1/me" basic_auth = True settings = app_settings.PROVIDERS.get(provider_id, {}) # Allow custom User Agent to comply with reddit API limits headers = {"User-Agent": settings.get("USER_AGENT", "django-allauth-header")} def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"bearer {token.token}", **self.headers} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) # This only here because of weird response from the test suite if isinstance(resp, list): resp = resp[0] extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(RedditAdapter) oauth2_callback = OAuth2CallbackView.adapter_view(RedditAdapter) ================================================ FILE: allauth/socialaccount/providers/robinhood/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/robinhood/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.robinhood.views import RobinhoodOAuth2Adapter class RobinhoodAccount(ProviderAccount): def get_avatar_url(self): return None class RobinhoodProvider(OAuth2Provider): id = "robinhood" name = "Robinhood" account_class = RobinhoodAccount oauth2_adapter_class = RobinhoodOAuth2Adapter def get_default_scope(self): return ["read"] def extract_uid(self, data): return data["id"] def extract_common_fields(self, data): return dict(username=data.get("username")) provider_classes = [RobinhoodProvider] ================================================ FILE: allauth/socialaccount/providers/robinhood/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.robinhood.provider import RobinhoodProvider urlpatterns = default_urlpatterns(RobinhoodProvider) ================================================ FILE: allauth/socialaccount/providers/robinhood/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class RobinhoodOAuth2Adapter(OAuth2Adapter): provider_id = "robinhood" @property def authorize_url(self): return "https://www.robinhood.com/oauth2/authorize/" @property def access_token_url(self): return "https://api.robinhood.com/oauth2/token/" @property def profile_url(self): return "https://api.robinhood.com/user/id/" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: response = sess.get(self.profile_url, headers=headers) extra_data = response.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(RobinhoodOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(RobinhoodOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/salesforce/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/salesforce/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount import providers from allauth.socialaccount.providers.base import AuthAction, ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.salesforce.views import SalesforceOAuth2Adapter class SalesforceAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("link") def get_avatar_url(self): return self.account.extra_data.get("picture") class SalesforceProvider(OAuth2Provider): id = "salesforce" name = "Salesforce" package = "allauth.socialaccount.providers.salesforce" account_class = SalesforceAccount oauth2_adapter_class = SalesforceOAuth2Adapter def get_default_scope(self): return ["id", "openid"] def get_auth_params_from_request(self, request, action): ret = super().get_auth_params_from_request(request, action) if action == AuthAction.REAUTHENTICATE: ret["approval_prompt"] = "force" return ret def extract_uid(self, data): return str(data["user_id"]) def extract_common_fields(self, data): return dict( email=data.get("email"), last_name=data.get("family_name"), first_name=data.get("given_name"), username=data.get("preferred_username"), ) def extract_email_addresses(self, data): # a salesforce user must have an email, but it might not be verified email = EmailAddress( email=data.get("email"), primary=True, verified=data.get("email_verified"), ) return [email] providers.registry.register(SalesforceProvider) ================================================ FILE: allauth/socialaccount/providers/salesforce/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.salesforce.provider import SalesforceProvider urlpatterns = default_urlpatterns(SalesforceProvider) ================================================ FILE: allauth/socialaccount/providers/salesforce/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class SalesforceOAuth2Adapter(OAuth2Adapter): provider_id = "salesforce" @property def base_url(self): return self.get_provider().app.key @property def authorize_url(self): return f"{self.base_url}/services/oauth2/authorize" @property def access_token_url(self): return f"{self.base_url}/services/oauth2/token" @property def userinfo_url(self): return f"{self.base_url}/services/oauth2/userinfo" def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: resp = sess.get(self.userinfo_url, params={"oauth_token": token.token}) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(SalesforceOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(SalesforceOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/saml/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/saml/provider.py ================================================ from django.http import HttpResponseRedirect from django.urls import reverse from django.utils.http import urlencode from allauth.socialaccount.providers.base import Provider, ProviderAccount class SAMLAccount(ProviderAccount): pass class SAMLProvider(Provider): id = "saml" name = "SAML" supports_redirect = True account_class = SAMLAccount default_attribute_mapping = { "uid": [ "urn:oasis:names:tc:SAML:attribute:subject-id", ], "email": [ "urn:oid:0.9.2342.19200300.100.1.3", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", ], "email_verified": [ "http://schemas.auth0.com/email_verified", ], "first_name": [ "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname", "urn:oid:2.5.4.42", ], "last_name": [ "urn:oid:2.5.4.4", ], "username": [ "http://schemas.auth0.com/nickname", ], } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.name = self.app.name or self.app.client_id or self.name def get_login_url(self, request, **kwargs): url = reverse("saml_login", kwargs={"organization_slug": self.app.client_id}) if kwargs: url = f"{url}?{urlencode(kwargs)}" return url def extract_extra_data(self, data): return data.get_attributes() def extract_uid(self, data): """https://docs.oasis-open.org/security/saml-subject-id-attr/v1.0/csprd01/saml-subject-id-attr-v1.0-csprd01.html Quotes: "While the Attributes defined in this profile have as a goal the explicit replacement of the element as a means of subject identification, it is certainly possible to compose them with existing NameID usage provided the same subject is being identified. This can also serve as a migration strategy for existing applications." "SAML does not define an identifier that meets all of these requirements well. It does standardize a kind of NameID termed “persistent” that meets some of them in the particular case of so-called “pairwise” identification, where an identifier varies by relying party. It has seen minimal adoption outside of a few contexts, and fails at the “compact” and “simple to handle” criteria above, on top of the disadvantages inherent with all NameID usage." Overall, our strategy is to prefer a uid resulting from explicit attribute mappings, and only if there is no such uid fallback to the NameID. """ uid = self._extract(data).get("uid") if uid is None: uid = data.get_nameid() return uid def extract_common_fields(self, data): ret = self._extract(data) ret.pop("uid", None) return ret def _extract(self, data): provider_config = self.app.settings raw_attributes = data.get_attributes() attributes = {} attribute_mapping = provider_config.get( "attribute_mapping", self.default_attribute_mapping ) # map configured provider attributes for key, provider_keys in attribute_mapping.items(): if isinstance(provider_keys, str): provider_keys = [provider_keys] for provider_key in provider_keys: attribute_list = raw_attributes.get(provider_key, None) if attribute_list is not None and len(attribute_list) > 0: attributes[key] = attribute_list[0] break email_verified = attributes.get("email_verified") if email_verified: email_verified = email_verified.lower() in ["true", "1", "t", "y", "yes"] attributes["email_verified"] = email_verified # If we did not find an email, check if the NameID contains the email. if not attributes.get("email") and ( data.get_nameid_format() == "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" # Alternatively, if `use_id_for_email` is true, then we always interpret the nameID as email or provider_config.get("use_nameid_for_email", False) ): attributes["email"] = data.get_nameid() return attributes def redirect(self, request, process, next_url=None, data=None, **kwargs): from allauth.socialaccount.providers.saml.utils import build_auth auth = build_auth(request, self) # If we pass `return_to=None` `auth.login` will use the URL of the # current view. redirect = auth.login(return_to="") self.stash_redirect_state( request, process, next_url, data, state_id=auth.get_last_request_id(), **kwargs, ) return HttpResponseRedirect(redirect) provider_classes = [SAMLProvider] ================================================ FILE: allauth/socialaccount/providers/saml/urls.py ================================================ from django.urls import include, path, re_path from . import views urlpatterns = [ re_path( r"^saml/(?P[^/]+)/", include( [ path( "acs/", views.acs, name="saml_acs", ), path( "acs/finish/", views.finish_acs, name="saml_finish_acs", ), path( "sls/", views.sls, name="saml_sls", ), path( "metadata/", views.metadata, name="saml_metadata", ), path( "login/", views.login, name="saml_login", ), ] ), ) ] ================================================ FILE: allauth/socialaccount/providers/saml/utils.py ================================================ from urllib.parse import urlparse from django.core.cache import cache from django.core.exceptions import ImproperlyConfigured from django.http import Http404 from django.urls import reverse from django.utils.http import urlencode from onelogin.saml2.auth import OneLogin_Saml2_Auth from onelogin.saml2.constants import OneLogin_Saml2_Constants from onelogin.saml2.idp_metadata_parser import OneLogin_Saml2_IdPMetadataParser from allauth.account.adapter import get_adapter as get_account_adapter from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.models import SocialApp from allauth.socialaccount.providers.saml.provider import SAMLProvider def get_app_or_404(request, organization_slug): adapter = get_adapter() try: return adapter.get_app( request, provider=SAMLProvider.id, client_id=organization_slug ) except SocialApp.DoesNotExist: raise Http404(f"no SocialApp found with client_id={organization_slug}") def prepare_django_request(request) -> dict: result = { "https": "on" if request.is_secure() else "off", "http_host": request.META["HTTP_HOST"], "script_name": request.META.get("SCRIPT_NAME"), "path_info": request.META["PATH_INFO"], "get_data": request.GET.copy(), # 'lowercase_urlencoding': True, "post_data": request.POST.copy(), } return result def build_sp_config(request, provider_config, org) -> dict: acs_url = request.build_absolute_uri(reverse("saml_acs", args=[org])) sls_url = request.build_absolute_uri(reverse("saml_sls", args=[org])) metadata_url = request.build_absolute_uri(reverse("saml_metadata", args=[org])) # SP entity ID generated with the following precedence: # 1. Explicitly configured SP via the SocialApp.settings # 2. Fallback to the SAML metadata urlpattern _sp_config = provider_config.get("sp", {}) sp_entity_id = _sp_config.get("entity_id") sp_config = { "entityId": sp_entity_id or metadata_url, "assertionConsumerService": { "url": acs_url, "binding": OneLogin_Saml2_Constants.BINDING_HTTP_POST, }, "singleLogoutService": { "url": sls_url, "binding": OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT, }, } avd = provider_config.get("advanced", {}) if avd.get("x509cert") is not None: sp_config["x509cert"] = avd["x509cert"] if avd.get("x509cert_new"): sp_config["x509certNew"] = avd["x509cert_new"] if avd.get("private_key") is not None: sp_config["privateKey"] = avd["private_key"] if avd.get("name_id_format") is not None: sp_config["NameIDFormat"] = avd["name_id_format"] return sp_config def fetch_metadata_url_config(idp_config): metadata_url = idp_config["metadata_url"] entity_id = idp_config["entity_id"] cache_key = f"saml.metadata.{metadata_url}.{entity_id}" saml_config = cache.get(cache_key) if saml_config is None: saml_config = OneLogin_Saml2_IdPMetadataParser.parse_remote( metadata_url, entity_id=entity_id, timeout=idp_config.get("metadata_request_timeout", 10), ) cache.set( cache_key, saml_config, idp_config.get("metadata_cache_timeout", 60 * 60 * 4), ) return saml_config def build_saml_config(request, provider_config, org) -> dict: avd = provider_config.get("advanced", {}) security_config = { "authnRequestsSigned": avd.get("authn_request_signed", False), "digestAlgorithm": avd.get("digest_algorithm", OneLogin_Saml2_Constants.SHA256), "logoutRequestSigned": avd.get("logout_request_signed", False), "logoutResponseSigned": avd.get("logout_response_signed", False), "requestedAuthnContext": False, "signatureAlgorithm": avd.get( "signature_algorithm", OneLogin_Saml2_Constants.RSA_SHA256 ), "signMetadata": avd.get("metadata_signed", False), "wantAssertionsEncrypted": avd.get("want_assertion_encrypted", False), "wantAssertionsSigned": avd.get("want_assertion_signed", False), "wantMessagesSigned": avd.get("want_message_signed", False), "nameIdEncrypted": avd.get("name_id_encrypted", False), "wantNameIdEncrypted": avd.get("want_name_id_encrypted", False), "allowSingleLabelDomains": avd.get("allow_single_label_domains", False), "rejectDeprecatedAlgorithm": avd.get("reject_deprecated_algorithm", True), "wantNameId": avd.get("want_name_id", False), "wantAttributeStatement": avd.get("want_attribute_statement", True), "allowRepeatAttributeName": avd.get("allow_repeat_attribute_name", True), "metadataValidUntil": avd.get("metadata_valid_until", None), "metadataCacheDuration": avd.get("metadata_cache_duration", None), } saml_config = { "strict": avd.get("strict", True), "security": security_config, } contact_person = provider_config.get("contact_person") if contact_person: saml_config["contactPerson"] = contact_person organization = provider_config.get("organization") if organization: saml_config["organization"] = organization idp = provider_config.get("idp") if idp is None: raise ImproperlyConfigured("`idp` missing") metadata_url = idp.get("metadata_url") if metadata_url: meta_config = fetch_metadata_url_config(idp) saml_config["idp"] = meta_config["idp"] else: saml_config["idp"] = { "entityId": idp["entity_id"], "x509cert": idp["x509cert"], "singleSignOnService": {"url": idp["sso_url"]}, } slo_url = idp.get("slo_url") if slo_url: saml_config["idp"]["singleLogoutService"] = {"url": slo_url} saml_config["sp"] = build_sp_config(request, provider_config, org) return saml_config def encode_relay_state(state) -> str: params = {"state": state} return urlencode(params) def decode_relay_state(relay_state): """According to the spec, RelayState need not be a URL, yet, ``onelogin.saml2` exposes it as ``return_to -- The target URL the user should be redirected to after login``. Also, for an IdP initiated login sometimes a URL is used. """ next_url = None if relay_state: parts = urlparse(relay_state) if parts.scheme or parts.netloc or (parts.path and parts.path.startswith("/")): if get_account_adapter().is_safe_url(relay_state): next_url = relay_state return next_url def build_auth(request, provider): req = prepare_django_request(request) config = build_saml_config(request, provider.app.settings, provider.app.client_id) auth = OneLogin_Saml2_Auth(req, config) return auth ================================================ FILE: allauth/socialaccount/providers/saml/views.py ================================================ import binascii import logging from http import HTTPStatus from django.http import HttpRequest, HttpResponse, HttpResponseRedirect, JsonResponse from django.urls import reverse from django.utils.decorators import method_decorator from django.views import View from django.views.decorators.csrf import csrf_exempt from onelogin.saml2.auth import OneLogin_Saml2_Settings from onelogin.saml2.errors import OneLogin_Saml2_Error from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.internal.decorators import login_not_required from allauth.core.internal import httpkit from allauth.socialaccount.helpers import ( complete_social_login, render_authentication_error, ) from allauth.socialaccount.providers.base.constants import AuthError, AuthProcess from allauth.socialaccount.providers.base.views import BaseLoginView from allauth.socialaccount.sessions import LoginSession from .utils import build_auth, build_saml_config, decode_relay_state, get_app_or_404 logger = logging.getLogger(__name__) class SAMLViewMixin: def get_app(self, organization_slug): app = get_app_or_404(self.request, organization_slug) return app def get_provider(self, organization_slug): app = self.get_app(organization_slug) return app.get_provider(self.request) @method_decorator(csrf_exempt, name="dispatch") @method_decorator(login_not_required, name="dispatch") class ACSView(SAMLViewMixin, View): def dispatch(self, request, organization_slug) -> HttpResponse: url = reverse( "saml_finish_acs", kwargs={"organization_slug": organization_slug}, ) response = HttpResponseRedirect(url) acs_session = LoginSession(request, "saml_acs_session", "saml-acs-session") acs_session.store.update({"request": httpkit.serialize_request(request)}) acs_session.save(response) return response acs = ACSView.as_view() @method_decorator(login_not_required, name="dispatch") class FinishACSView(SAMLViewMixin, View): def dispatch(self, request, organization_slug) -> HttpResponse: provider = self.get_provider(organization_slug) acs_session = LoginSession(request, "saml_acs_session", "saml-acs-session") acs_request = None acs_request_data = acs_session.store.get("request") if acs_request_data: acs_request = httpkit.deserialize_request(acs_request_data, HttpRequest()) acs_session.delete() if not acs_request: logger.error("Unable to finish login, SAML ACS session missing") return render_authentication_error(request, provider) auth = build_auth(acs_request, provider) error_reason = None errors = [] try: # We're doing the check for a valid `InResponeTo` ourselves later on # (*) by checking if there is a matching state stashed. auth.process_response(request_id=None) except binascii.Error: errors = ["invalid_response"] error_reason = "Invalid response" except OneLogin_Saml2_Error as e: errors = ["error"] error_reason = str(e) if not errors: errors = auth.get_errors() if errors: # e.g. ['invalid_response'] error_reason = auth.get_last_error_reason() or error_reason logger.error( "Error processing SAML ACS response: %s: %s" % (", ".join(errors), error_reason) ) return render_authentication_error( request, provider, extra_context={ "saml_errors": errors, "saml_last_error_reason": error_reason, }, ) if not auth.is_authenticated(): return render_authentication_error( request, provider, error=AuthError.CANCELLED ) login = provider.sociallogin_from_response(request, auth) # (*) If we (the SP) initiated the login, there should be a matching # state. state_id = auth.get_last_response_in_response_to() if state_id: login.state = provider.unstash_redirect_state(request, state_id) else: # IdP initiated SSO reject = provider.app.settings.get("advanced", {}).get( "reject_idp_initiated_sso", True ) if reject: logger.error("IdP initiated SSO rejected") return render_authentication_error(request, provider) next_url = decode_relay_state(acs_request.POST.get("RelayState")) login.state["process"] = AuthProcess.LOGIN if next_url: login.state["next"] = next_url return complete_social_login(request, login) finish_acs = FinishACSView.as_view() @method_decorator(csrf_exempt, name="dispatch") @method_decorator(login_not_required, name="dispatch") class SLSView(SAMLViewMixin, View): def dispatch(self, request, organization_slug) -> HttpResponse: provider = self.get_provider(organization_slug) auth = build_auth(self.request, provider) should_logout = request.user.is_authenticated account_adapter = get_account_adapter(request) def force_logout(): account_adapter.logout(request) redirect_to = None error_reason = None try: redirect_to = auth.process_slo( delete_session_cb=force_logout, keep_local_session=not should_logout ) except OneLogin_Saml2_Error as e: error_reason = str(e) errors = auth.get_errors() if errors: error_reason = auth.get_last_error_reason() or error_reason logger.error( "Error processing SAML SLS response: %s: %s" % (", ".join(errors), error_reason) ) resp = HttpResponse(error_reason, content_type="text/plain") resp.status_code = HTTPStatus.BAD_REQUEST return resp if not redirect_to: redirect_to = account_adapter.get_logout_redirect_url(request) return HttpResponseRedirect(redirect_to) sls = SLSView.as_view() @method_decorator(login_not_required, name="dispatch") class MetadataView(SAMLViewMixin, View): def dispatch(self, request, organization_slug) -> HttpResponse: provider = self.get_provider(organization_slug) config = build_saml_config( self.request, provider.app.settings, organization_slug ) saml_settings = OneLogin_Saml2_Settings( settings=config, sp_validation_only=True ) metadata = saml_settings.get_sp_metadata() errors = saml_settings.validate_metadata(metadata) if len(errors) > 0: resp = JsonResponse({"errors": errors}) resp.status_code = HTTPStatus.INTERNAL_SERVER_ERROR return resp return HttpResponse(content=metadata, content_type="text/xml") metadata = MetadataView.as_view() @method_decorator(login_not_required, name="dispatch") class LoginView(SAMLViewMixin, BaseLoginView): def get_provider(self): app = self.get_app(self.kwargs["organization_slug"]) return app.get_provider(self.request) login = LoginView.as_view() ================================================ FILE: allauth/socialaccount/providers/sharefile/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/sharefile/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.sharefile.views import ShareFileOAuth2Adapter class ShareFileAccount(ProviderAccount): pass class ShareFileProvider(OAuth2Provider): id = "sharefile" name = "ShareFile" account_class = ShareFileAccount oauth2_adapter_class = ShareFileOAuth2Adapter def extract_uid(self, data): return str(data.get("Id", "")) def extract_common_fields(self, data): return dict( email=data.get("Email", ""), username=data.get("Username", ""), name=data.get("FullName", ""), ) provider_classes = [ShareFileProvider] ================================================ FILE: allauth/socialaccount/providers/sharefile/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.sharefile.provider import ShareFileProvider urlpatterns = default_urlpatterns(ShareFileProvider) ================================================ FILE: allauth/socialaccount/providers/sharefile/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class ShareFileOAuth2Adapter(OAuth2Adapter): provider_id = "sharefile" settings = app_settings.PROVIDERS.get(provider_id, {}) subdomain = settings.get("SUBDOMAIN", "secure") apicp = settings.get("APICP", "sharefile.com") provider_default_url = settings.get("DEFAULT_URL", "https://secure.sharefile.com") provider_default_api_url = f"https://{subdomain}.sf-api.com" provider_api_version = "v3" access_token_url = f"https://{subdomain}.{apicp}/oauth/token" refresh_token_url = f"https://{subdomain}.{apicp}/oauth/token" authorize_url = f"{provider_default_url}/oauth/authorize" profile_url = f"{provider_default_api_url}/sf/{provider_api_version}/Users" def complete_login(self, request, app, token, response): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(ShareFileOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(ShareFileOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/shopify/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/shopify/provider.py ================================================ from typing import Any from django.conf import settings from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.shopify.views import ShopifyOAuth2Adapter class ShopifyAccount(ProviderAccount): def get_user_data(self): return self.account.extra_data.get("shop", {}) class ShopifyProvider(OAuth2Provider): id = "shopify" name = "Shopify" account_class = ShopifyAccount oauth2_adapter_class = ShopifyOAuth2Adapter @property def is_per_user(self): grant_options = ( getattr(settings, "SOCIALACCOUNT_PROVIDERS", {}) .get("shopify", {}) .get("AUTH_PARAMS", {}) .get("grant_options[]", "") ) return grant_options.lower().strip() == "per-user" def get_auth_params_from_request(self, request, action): ret = super().get_auth_params_from_request(request, action) shop = request.GET.get("shop", None) if shop: ret.update({"shop": shop}) return ret def get_default_scope(self): return ["read_orders", "read_products"] def extract_uid(self, data): if self.is_per_user: return str(data["associated_user"]["id"]) else: return str(data["shop"]["id"]) def extract_common_fields(self, data): if self.is_per_user: return dict( email=data["associated_user"]["email"], first_name=data["associated_user"]["first_name"], last_name=data["associated_user"]["last_name"], ) else: # See: https://docs.shopify.com/api/shop # Without online mode, User is only available with Shopify Plus, # email is the only common field return dict(email=data["shop"]["email"]) def extract_email_addresses(self, data: dict[str, Any]) -> list[EmailAddress]: ret = [] email = None email_verified = False if self.is_per_user: if associated_user := data.get("associated_user"): email = associated_user.get("email") email_verified = associated_user.get("email_verified", False) else: # The documentation of Shopify does not state anything about # verified email addresses. email = (data.get("shop") or {}).get("email") email_verified = False if email: ret.append( EmailAddress( email=email, verified=email_verified, primary=True, ) ) return ret provider_classes = [ShopifyProvider] ================================================ FILE: allauth/socialaccount/providers/shopify/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.shopify.provider import ShopifyProvider urlpatterns = default_urlpatterns(ShopifyProvider) ================================================ FILE: allauth/socialaccount/providers/shopify/views.py ================================================ import re from django.conf import settings from django.http import HttpResponse, HttpResponseBadRequest from allauth.core.exceptions import ImmediateHttpResponse from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class ShopifyOAuth2Adapter(OAuth2Adapter): provider_id = "shopify" scope_delimiter = "," def _shop_domain(self): shop = self.request.GET.get("shop", "") if "." not in shop: shop = f"{shop}.myshopify.com" # Ensure the provided hostname parameter is a valid hostname, # ends with myshopify.com, and does not contain characters # other than letters (a-z), numbers (0-9), dots, and hyphens. if not re.match(r"^[a-z0-9-]+\.myshopify\.com$", shop): raise ImmediateHttpResponse( HttpResponseBadRequest("Invalid `shop` parameter") ) return shop def _shop_url(self, path): shop = self._shop_domain() return f"https://{shop}{path}" @property def access_token_url(self): return self._shop_url("/admin/oauth/access_token") @property def authorize_url(self): return self._shop_url("/admin/oauth/authorize") @property def profile_url(self): return self._shop_url("/admin/shop.json") def complete_login(self, request, app, token, **kwargs): headers = {"X-Shopify-Access-Token": f"{token.token}"} with get_adapter().get_requests_session() as sess: response = sess.get(self.profile_url, headers=headers) extra_data = response.json() associated_user = kwargs["response"].get("associated_user") if associated_user: extra_data["associated_user"] = associated_user return self.get_provider().sociallogin_from_response(request, extra_data) class ShopifyOAuth2LoginView(OAuth2LoginView): def dispatch(self, request, *args, **kwargs): is_embedded = ( getattr(settings, "SOCIALACCOUNT_PROVIDERS", {}) .get("shopify", {}) .get("IS_EMBEDDED", False) ) if is_embedded: # TODO: This bypasses LOGIN_ON_GET, but: # # The Embedded App SDK (EASDK) and backwards compatibility layer # are being removed from Shopify on January 1, 2022. # # So this needs to be dropped/revisitted anyway. response = super().dispatch(request, *args, **kwargs) """ Shopify embedded apps (that run within an iFrame) require a JS (not server) redirect for starting the oauth2 process. See Also: https://help.shopify.com/api/sdks/embedded-app-sdk/getting-started#oauth """ js = ( '" ) response = HttpResponse(content=js) # Because this view will be within shopify's iframe response.xframe_options_exempt = True return response return super().dispatch(request, *args, **kwargs) oauth2_login = ShopifyOAuth2LoginView.adapter_view(ShopifyOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(ShopifyOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/slack/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/slack/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/slack/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.slack.views import SlackOAuth2Adapter class SlackAccount(ProviderAccount): def get_avatar_url(self): return self.account.extra_data.get("user", {}).get("image_192") class SlackProvider(OAuth2Provider): id = "slack" name = "Slack" account_class = SlackAccount oauth2_adapter_class = SlackOAuth2Adapter def extract_uid(self, data): team_id = data.get("https://slack.com/team_id") user_id = data.get("https://slack.com/user_id") if not (team_id and user_id): team_id = data.get("team").get("id") user_id = data.get("user").get("id") return f"{team_id!s}_{user_id!s}" def extract_common_fields(self, data): user = data.get("user", {}) return {"name": user.get("name"), "email": user.get("email", None)} def extract_email_addresses(self, data): ret = [] email = data.get("email") if email: verified = data.get("email_verified") ret.append(EmailAddress(email=email, verified=verified, primary=True)) return ret def get_default_scope(self): return ["openid", "profile", "email"] provider_classes = [SlackProvider] ================================================ FILE: allauth/socialaccount/providers/slack/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.slack.provider import SlackProvider urlpatterns = default_urlpatterns(SlackProvider) ================================================ FILE: allauth/socialaccount/providers/slack/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Error from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class SlackOAuth2Adapter(OAuth2Adapter): provider_id = "slack" access_token_url = "https://slack.com/api/openid.connect.token" # nosec authorize_url = "https://slack.com//openid/connect/authorize" identity_url = "https://slack.com/api/openid.connect.userInfo" def complete_login(self, request, app, token, **kwargs): extra_data = self.get_data(token.token) return self.get_provider().sociallogin_from_response(request, extra_data) def get_data(self, token): # Verify the user first headers = {"Authorization": f"Bearer {token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.identity_url, headers=headers) data = resp.json() if not data.get("ok"): raise OAuth2Error() return data oauth2_login = OAuth2LoginView.adapter_view(SlackOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(SlackOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/snapchat/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/snapchat/constants.py ================================================ PROVIDER_ID = "snapchat" class Scope: EXTERNAL_ID = "https://auth.snapchat.com/oauth2/api/user.external_id" DISPLAY_NAME = "https://auth.snapchat.com/oauth2/api/user.display_name" BITMOJI = "https://auth.snapchat.com/oauth2/api/user.bitmoji.avatar" ================================================ FILE: allauth/socialaccount/providers/snapchat/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/snapchat/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.snapchat.constants import PROVIDER_ID, Scope from allauth.socialaccount.providers.snapchat.views import SnapchatOAuth2Adapter class SnapchatAccount(ProviderAccount): def get_user_data(self): return self.account.extra_data.get("data", {}).get("me", {}) class SnapchatProvider(OAuth2Provider): id = PROVIDER_ID name = "Snapchat" account_class = SnapchatAccount oauth2_adapter_class = SnapchatOAuth2Adapter def get_default_scope(self): scope = [Scope.EXTERNAL_ID, Scope.DISPLAY_NAME] return scope def extract_uid(self, data): return str(data.get("data").get("me").get("externalId")) def extract_common_fields(self, data): user = data.get("data", {}).get("me") return {"name": user.get("displayName")} provider_classes = [SnapchatProvider] ================================================ FILE: allauth/socialaccount/providers/snapchat/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.snapchat.provider import SnapchatProvider urlpatterns = default_urlpatterns(SnapchatProvider) ================================================ FILE: allauth/socialaccount/providers/snapchat/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Error from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) from allauth.socialaccount.providers.snapchat.constants import PROVIDER_ID, Scope class SnapchatOAuth2Adapter(OAuth2Adapter): provider_id = PROVIDER_ID access_token_url = "https://accounts.snapchat.com/accounts/oauth2/token" # nosec authorize_url = "https://accounts.snapchat.com/accounts/oauth2/auth" identity_url = "https://api.snapkit.com/v1/me" def complete_login(self, request, app, token, **kwargs): extra_data = self.get_data(token.token) return self.get_provider().sociallogin_from_response(request, extra_data) def get_data(self, token): settings = app_settings.PROVIDERS.get(self.provider_id, {}) provider_scope = settings.get( "SCOPE", "['https://auth.snapchat.com/oauth2/api/user.external_id', 'https://auth.snapchat.com/oauth2/api/user.display_name']", ) hed = { "Authorization": f"Bearer {token}", "Content-Type": "application/json;charset=UTF-8", } if Scope.BITMOJI in provider_scope: data = {"query": "{ me { externalId displayName bitmoji { avatar id } } }"} else: data = {"query": "{ me { externalId displayName } }"} with get_adapter().get_requests_session() as sess: resp = sess.post(self.identity_url, headers=hed, json=data) resp.raise_for_status() data = resp.json() if not data.get("data"): raise OAuth2Error() return data oauth2_login = OAuth2LoginView.adapter_view(SnapchatOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(SnapchatOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/soundcloud/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/soundcloud/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.soundcloud.views import SoundCloudOAuth2Adapter class SoundCloudAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("permalink_url") def get_avatar_url(self): return self.account.extra_data.get("avatar_url") class SoundCloudProvider(OAuth2Provider): id = "soundcloud" name = "SoundCloud" account_class = SoundCloudAccount oauth2_adapter_class = SoundCloudOAuth2Adapter def extract_uid(self, data): return str(data["urn"]) def extract_common_fields(self, data): return dict( name=data.get("full_name"), username=data.get("username"), email=data.get("email"), ) provider_classes = [SoundCloudProvider] ================================================ FILE: allauth/socialaccount/providers/soundcloud/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.soundcloud.provider import SoundCloudProvider urlpatterns = default_urlpatterns(SoundCloudProvider) ================================================ FILE: allauth/socialaccount/providers/soundcloud/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class SoundCloudOAuth2Adapter(OAuth2Adapter): provider_id = "soundcloud" access_token_url = "https://api.soundcloud.com/oauth2/token" # nosec authorize_url = "https://soundcloud.com/connect" profile_url = "https://api.soundcloud.com/me.json" def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, params={"oauth_token": token.token}) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(SoundCloudOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(SoundCloudOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/spotify/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/spotify/provider.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.spotify.views import SpotifyOAuth2Adapter class SpotifyAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("external_urls").get("spotify") def get_avatar_url(self): try: return self.account.extra_data.get("images")[0].get("url") except IndexError: return None class SpotifyOAuth2Provider(OAuth2Provider): id = "spotify" name = "Spotify" account_class = SpotifyAccount oauth2_adapter_class = SpotifyOAuth2Adapter def extract_uid(self, data): return data["id"] def extract_common_fields(self, data): return dict(name=data.get("display_name"), email=data.get("email")) def get_default_scope(self): scope = [] if app_settings.QUERY_EMAIL: scope.append("user-read-email") return scope provider_classes = [SpotifyOAuth2Provider] ================================================ FILE: allauth/socialaccount/providers/spotify/urls.py ================================================ from allauth.socialaccount.providers.oauth.urls import default_urlpatterns from allauth.socialaccount.providers.spotify.provider import SpotifyOAuth2Provider urlpatterns = default_urlpatterns(SpotifyOAuth2Provider) ================================================ FILE: allauth/socialaccount/providers/spotify/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class SpotifyOAuth2Adapter(OAuth2Adapter): provider_id = "spotify" access_token_url = "https://accounts.spotify.com/api/token" # nosec authorize_url = "https://accounts.spotify.com/authorize" profile_url = "https://api.spotify.com/v1/me" def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, params={"access_token": token.token}) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth_login = OAuth2LoginView.adapter_view(SpotifyOAuth2Adapter) oauth_callback = OAuth2CallbackView.adapter_view(SpotifyOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/stackexchange/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/stackexchange/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.stackexchange.views import ( StackExchangeOAuth2Adapter, ) class StackExchangeAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("html_url") def get_avatar_url(self): return self.account.extra_data.get("avatar_url") class StackExchangeProvider(OAuth2Provider): id = "stackexchange" name = "Stack Exchange" account_class = StackExchangeAccount oauth2_adapter_class = StackExchangeOAuth2Adapter def get_site(self): settings = self.get_settings() return settings.get("SITE", "stackoverflow") def extract_uid(self, data): # `user_id` varies if you use the same account for # e.g. StackOverflow and ServerFault. Therefore, we pick # `account_id`. uid = str(data["account_id"]) return uid def extract_common_fields(self, data): return dict(username=data.get("display_name")) provider_classes = [StackExchangeProvider] ================================================ FILE: allauth/socialaccount/providers/stackexchange/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.stackexchange.provider import StackExchangeProvider urlpatterns = default_urlpatterns(StackExchangeProvider) ================================================ FILE: allauth/socialaccount/providers/stackexchange/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class StackExchangeOAuth2Adapter(OAuth2Adapter): provider_id = "stackexchange" access_token_url = "https://stackexchange.com/oauth/access_token" # nosec authorize_url = "https://stackexchange.com/oauth" profile_url = "https://api.stackexchange.com/2.1/me" def complete_login(self, request, app, token, **kwargs): provider = self.get_provider() site = provider.get_site() with get_adapter().get_requests_session() as sess: params = {"access_token": token.token, "key": app.key, "site": site} resp = sess.get(self.profile_url, params=params) resp.raise_for_status() extra_data = resp.json()["items"][0] return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(StackExchangeOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(StackExchangeOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/steam/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/steam/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/steam/provider.py ================================================ from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.http import HttpResponseRedirect from django.urls import reverse from django.utils.http import urlencode from openid.consumer.discover import DiscoveryFailure from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.helpers import render_authentication_error from allauth.socialaccount.providers.openid.provider import ( OpenIDAccount, OpenIDProvider, ) from allauth.socialaccount.providers.openid.views import _openid_consumer if "allauth.socialaccount.providers.openid" not in settings.INSTALLED_APPS: raise ImproperlyConfigured( "The steam provider requires 'allauth.socialaccount.providers.openid' to be installed" ) STEAM_OPENID_URL = "https://steamcommunity.com/openid" class SteamAccount(OpenIDAccount): def to_str(self): dflt = super().to_str() return self.account.extra_data.get("personaname", dflt) def get_profile_url(self): return self.account.extra_data.get("profileurl") def get_avatar_url(self): return ( self.account.extra_data.get("avatarfull") or self.account.extra_data.get("avatarmedium") or self.account.extra_data.get("avatar") ) def extract_steam_id(url): prefix = "https://steamcommunity.com/openid/id/" if not url.startswith(prefix): raise ValueError(url) return url[len(prefix) :] def request_steam_account_summary(api_key, steam_id): api_base = "https://api.steampowered.com/" method = "ISteamUser/GetPlayerSummaries/v0002/" params = {"key": api_key, "steamids": steam_id} with get_adapter().get_requests_session() as sess: resp = sess.get(api_base + method, params=params) resp.raise_for_status() data = resp.json() playerlist = data.get("response", {}).get("players", []) return playerlist[0] if playerlist else {"steamid": steam_id} class SteamOpenIDProvider(OpenIDProvider): id = "steam" name = "Steam" account_class = SteamAccount uses_apps = True supports_redirect = True def __init__(self, request, app=None): if app is None: app = get_adapter().get_app(request, self.id) super().__init__(request, app=app) def get_login_url(self, request, **kwargs): url = reverse("steam_login") if kwargs: url += f"?{urlencode(kwargs)}" return url def sociallogin_from_response(self, request, response): steam_id = extract_steam_id(response.identity_url) steam_api_key = self.app.secret response._extra = request_steam_account_summary(steam_api_key, steam_id) return super().sociallogin_from_response(request, response) def extract_uid(self, response): return response._extra["steamid"] def extract_extra_data(self, response): return response._extra.copy() def extract_common_fields(self, response): full_name = response._extra.get("realname", "").strip() if full_name.count(" ") == 1: first_name, last_name = full_name.split() else: first_name, last_name = full_name, "" username = response._extra.get("personaname", "") return { "username": username or response._extra["steamid"], "first_name": first_name, "last_name": last_name, "full_name": full_name, } def get_realm(self, request): return self.get_settings().get("REALM", request.build_absolute_uri("/")) def redirect(self, request, process, next_url=None, data=None, **kwargs): endpoint = STEAM_OPENID_URL realm = self.get_realm(request) try: client = _openid_consumer(request, self, endpoint) auth_request = client.begin(endpoint) except (UnicodeDecodeError, DiscoveryFailure) as e: return render_authentication_error(request, self, exception=e) self.stash_redirect_state(request, process, next_url, data, **kwargs) redirect_url = auth_request.redirectURL( realm, request.build_absolute_uri(reverse("steam_callback")) ) return HttpResponseRedirect(redirect_url) provider_classes = [SteamOpenIDProvider] ================================================ FILE: allauth/socialaccount/providers/steam/urls.py ================================================ from django.urls import path from . import views urlpatterns = [ path("steam/login/", views.steam_login, name="steam_login"), path("steam/callback/", views.steam_callback, name="steam_callback"), ] ================================================ FILE: allauth/socialaccount/providers/steam/views.py ================================================ """ OpenID Adapter for Steam The Steam login API is simple OpenID but requires extra API calls for basic resources such as usernames. Resources: * Steam Web API Documentation https://steamcommunity.com/dev * Steam Partner API documentation https://partner.steamgames.com/doc/features/auth#website """ from django.utils.decorators import method_decorator from allauth.account.internal.decorators import login_not_required from allauth.socialaccount.providers.base.views import BaseLoginView from allauth.socialaccount.providers.openid.views import OpenIDCallbackView from allauth.socialaccount.providers.steam.provider import SteamOpenIDProvider @method_decorator(login_not_required, name="dispatch") class SteamOpenIDLoginView(BaseLoginView): provider_id = SteamOpenIDProvider.id class SteamOpenIDCallbackView(OpenIDCallbackView): provider_class = SteamOpenIDProvider steam_login = SteamOpenIDLoginView.as_view() steam_callback = SteamOpenIDCallbackView.as_view() ================================================ FILE: allauth/socialaccount/providers/stocktwits/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/stocktwits/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.stocktwits.views import StocktwitsOAuth2Adapter class StocktwitsAccount(ProviderAccount): def get_avatar_url(self): return self.account.extra_data.get("user", {}).get("avatar_url_ssl") def get_user_data(self): return self.account.extra_data.get("user", {}) class StocktwitsProvider(OAuth2Provider): id = "stocktwits" name = "Stocktwits" account_class = StocktwitsAccount oauth2_adapter_class = StocktwitsOAuth2Adapter def extract_uid(self, data): return str(data["user"]["id"]) def extract_common_fields(self, data): return dict( full_name=data.get("user", {}).get("name"), ) provider_classes = [StocktwitsProvider] ================================================ FILE: allauth/socialaccount/providers/stocktwits/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.stocktwits.provider import StocktwitsProvider urlpatterns = default_urlpatterns(StocktwitsProvider) ================================================ FILE: allauth/socialaccount/providers/stocktwits/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class StocktwitsOAuth2Adapter(OAuth2Adapter): provider_id = "stocktwits" access_token_url = "https://api.stocktwits.com/api/2/oauth/token" # nosec authorize_url = "https://api.stocktwits.com/api/2/oauth/authorize" profile_url = "https://api.stocktwits.com/api/2/streams/user/{user}.json" scope_delimiter = "," def complete_login(self, request, app, token, **kwargs): user_id = kwargs.get("response").get("user_id") with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url.format(user=user_id)) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(StocktwitsOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(StocktwitsOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/strava/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/strava/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.strava.views import StravaOAuth2Adapter class StravaAccount(ProviderAccount): def get_profile_url(self): id = self.account.extra_data.get("id") if id: return f"https://www.strava.com/athletes/{id}" return None def get_avatar_url(self): avatar = self.account.extra_data.get("profile") if avatar and avatar != "avatar/athlete/large.png": return avatar return None class StravaProvider(OAuth2Provider): id = "strava" name = "Strava" account_class = StravaAccount oauth2_adapter_class = StravaOAuth2Adapter def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): extra_common = super().extract_common_fields(data) firstname = data.get("firstname") lastname = data.get("lastname") name = " ".join(part for part in (firstname, lastname) if part) extra_common.update( username=data.get("username"), email=data.get("email"), first_name=firstname, last_name=lastname, name=name.strip(), ) return extra_common def get_default_scope(self): return ["read,activity:read"] provider_classes = [StravaProvider] ================================================ FILE: allauth/socialaccount/providers/strava/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.strava.provider import StravaProvider urlpatterns = default_urlpatterns(StravaProvider) ================================================ FILE: allauth/socialaccount/providers/strava/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class StravaOAuth2Adapter(OAuth2Adapter): provider_id = "strava" access_token_url = "https://www.strava.com/oauth/token" # nosec authorize_url = "https://www.strava.com/oauth/authorize" profile_url = "https://www.strava.com/api/v3/athlete" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(StravaOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(StravaOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/stripe/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/stripe/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.stripe.views import StripeOAuth2Adapter class StripeAccount(ProviderAccount): def to_str(self): default = super().to_str() email = self.account.extra_data.get("email") business = self.account.extra_data.get("business_name") if business: return f"{email or default} ({business})" else: return email or default class StripeProvider(OAuth2Provider): id = "stripe" name = "Stripe" account_class = StripeAccount oauth2_adapter_class = StripeOAuth2Adapter def extract_uid(self, data): return data["id"] def extract_common_fields(self, data): return dict(name=data.get("display_name"), email=data.get("email")) def get_default_scope(self): return ["read_only"] provider_classes = [StripeProvider] ================================================ FILE: allauth/socialaccount/providers/stripe/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.stripe.provider import StripeProvider urlpatterns = default_urlpatterns(StripeProvider) ================================================ FILE: allauth/socialaccount/providers/stripe/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class StripeOAuth2Adapter(OAuth2Adapter): provider_id = "stripe" access_token_url = "https://connect.stripe.com/oauth/token" # nosec authorize_url = "https://connect.stripe.com/oauth/authorize" profile_url = "https://api.stripe.com/v1/accounts/%s" def complete_login(self, request, app, token, response, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: url = self.profile_url % response.get("stripe_user_id") resp = sess.get(url, headers=headers) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(StripeOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(StripeOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/telegram/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/telegram/provider.py ================================================ from django.http import HttpResponseRedirect from django.urls import reverse from django.utils.http import urlencode from allauth.socialaccount.providers.base import Provider, ProviderAccount class TelegramAccount(ProviderAccount): pass class TelegramProvider(Provider): id = "telegram" name = "Telegram" account_class = TelegramAccount supports_redirect = True def get_login_url(self, request, **kwargs): url = reverse("telegram_login") if kwargs: url = f"{url}?{urlencode(kwargs)}" return url def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): ret = {} if data.get("first_name"): ret["first_name"] = data.get("first_name") if data.get("last_name"): ret["last_name"] = data.get("last_name") if data.get("username"): ret["username"] = data.get("username") return ret def get_auth_date_validity(self): auth_date_validity = 30 settings = self.get_settings() if "AUTH_PARAMS" in settings: auth_date_validity = settings.get("AUTH_PARAMS").get( "auth_date_validity", auth_date_validity ) auth_date_validity = self.app.settings.get( "auth_date_validity", auth_date_validity ) return auth_date_validity def redirect(self, request, process, next_url=None, data=None, **kwargs): state = self.stash_redirect_state(request, process, next_url, data, **kwargs) return_to = request.build_absolute_uri( f"{reverse('telegram_callback')}?{urlencode({'state': state})}" ) url = "https://oauth.telegram.org/auth?" + urlencode( { "origin": request.build_absolute_uri("/"), "bot_id": self.app.client_id, "request_access": "write", "embed": "0", "return_to": return_to, } ) return HttpResponseRedirect(url) provider_classes = [TelegramProvider] ================================================ FILE: allauth/socialaccount/providers/telegram/static/telegram/js/telegram.js ================================================ /* global document, window */ (function () { 'use strict' const f = document.createElement('form') f.method = 'POST' f.action = '' const fragment = window.location.hash.substr(1) const fragmentParams = new URLSearchParams(fragment) for (const param of fragmentParams) { const d = document.createElement('input') d.type = 'hidden' d.name = param[0] d.value = param[1] f.appendChild(d) } document.body.appendChild(f) f.submit() })() ================================================ FILE: allauth/socialaccount/providers/telegram/templates/telegram/callback.html ================================================ {% load static %} ================================================ FILE: allauth/socialaccount/providers/telegram/urls.py ================================================ from django.urls import path from . import views urlpatterns = [ path("telegram/login/", views.login, name="telegram_login"), path("telegram/login/callback/", views.callback, name="telegram_callback"), ] ================================================ FILE: allauth/socialaccount/providers/telegram/views.py ================================================ import base64 import binascii import hashlib import hmac import json import time from django.shortcuts import render from django.utils.decorators import method_decorator from django.views import View from django.views.decorators.csrf import csrf_exempt from allauth.account.internal.decorators import login_not_required from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.helpers import ( complete_social_login, render_authentication_error, ) from allauth.socialaccount.providers.base.views import BaseLoginView from allauth.socialaccount.providers.telegram.provider import TelegramProvider @method_decorator(login_not_required, name="dispatch") class LoginView(BaseLoginView): provider_id = TelegramProvider.id login = LoginView.as_view() @method_decorator(csrf_exempt, name="dispatch") @method_decorator(login_not_required, name="dispatch") class CallbackView(View): def get(self, request): return render(request, "telegram/callback.html") def post(self, request): adapter = get_adapter() provider = adapter.get_provider(request, TelegramProvider.id) state_id = request.GET.get("state") if not state_id: return render_authentication_error( request, provider=provider, ) try: result = request.POST.get("tgAuthResult") padding = "=" * (4 - (len(result) % 4)) data = json.loads(base64.b64decode(result + padding)) if not isinstance(data, dict) or "hash" not in data: raise ValueError("Invalid tgAuthResult") except (binascii.Error, json.JSONDecodeError, ValueError) as e: return render_authentication_error( request, provider=provider, exception=e, extra_context={"state_id": state_id}, ) hash = data.pop("hash") payload = "\n".join(sorted([f"{k}={v}" for k, v in data.items()])) token = provider.app.secret token_sha256 = hashlib.sha256(token.encode()).digest() expected_hash = hmac.new( token_sha256, payload.encode(), hashlib.sha256 ).hexdigest() auth_date = int(data.pop("auth_date")) auth_date_validity = provider.get_auth_date_validity() if hash != expected_hash or time.time() - auth_date > auth_date_validity: return render_authentication_error( request, provider=provider, extra_context={"response": data, "state_id": state_id}, ) login = provider.sociallogin_from_response(request, data) login.state = provider.unstash_redirect_state(request, state_id) return complete_social_login(request, login) callback = CallbackView.as_view() ================================================ FILE: allauth/socialaccount/providers/tiktok/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/tiktok/client.py ================================================ from allauth.socialaccount.providers.oauth2.client import OAuth2Client class TikTokOAuth2Client(OAuth2Client): client_id_parameter = "client_key" ================================================ FILE: allauth/socialaccount/providers/tiktok/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.tiktok.scope import TikTokScope from allauth.socialaccount.providers.tiktok.views import TikTokOAuth2Adapter class TikTokAccount(ProviderAccount): def get_username(self): return self.account.extra_data.get("username") def get_display_name(self): return self.account.extra_data.get("display_name") def get_profile_url(self): return self.account.extra_data.get("profile_deep_link") def get_avatar_url(self): return self.account.extra_data.get("avatar_url") class TikTokProvider(OAuth2Provider): id = "tiktok" name = "TikTok" account_class = TikTokAccount oauth2_adapter_class = TikTokOAuth2Adapter pkce_enabled_default = False def extract_uid(self, data): return str(data["open_id"]) def extract_common_fields(self, data): # TikTok does not provide an email address return { "username": data.get("username") or data.get("display_name"), "name": data.get("display_name"), } def get_default_scope(self): # Requires LoginKit and Scopes with user.info.basic and user.info.profile enabled return [TikTokScope.user_info_basic.value, TikTokScope.user_info_profile.value] provider_classes = [TikTokProvider] ================================================ FILE: allauth/socialaccount/providers/tiktok/scope.py ================================================ from enum import Enum class TikTokScope(Enum): user_info_basic = "user.info.basic" user_info_profile = "user.info.profile" ================================================ FILE: allauth/socialaccount/providers/tiktok/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.tiktok.provider import TikTokProvider urlpatterns = default_urlpatterns(TikTokProvider) ================================================ FILE: allauth/socialaccount/providers/tiktok/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) from allauth.socialaccount.providers.tiktok.client import TikTokOAuth2Client from allauth.socialaccount.providers.tiktok.scope import TikTokScope class TikTokOAuth2Adapter(OAuth2Adapter): provider_id = "tiktok" access_token_url = "https://open.tiktokapis.com/v2/oauth/token/" # nosec authorize_url = "https://www.tiktok.com/v2/auth/authorize/" # https://developers.tiktok.com/doc/tiktok-api-v2-get-user-info/ profile_url = "https://open.tiktokapis.com/v2/user/info/" client_class = TikTokOAuth2Client scope_delimiter = "," def get_query_fields(self): fields = [] if TikTokScope.user_info_basic.value in self.get_provider().get_scope(): fields += ["open_id", "display_name", "avatar_url"] if TikTokScope.user_info_profile.value in self.get_provider().get_scope(): fields += ["username", "profile_deep_link"] return ",".join(fields) def complete_login(self, request, app, token, **kwargs): headers = { "Authorization": f"Bearer {token.token}", "Client-ID": app.client_id, } with get_adapter().get_requests_session() as sess: params = {"fields": self.get_query_fields()} response = sess.get(self.profile_url, headers=headers, params=params) response.raise_for_status() data = response.json() user_info = data.get("data", {}).get("user") return self.get_provider().sociallogin_from_response(request, user_info) oauth2_login = OAuth2LoginView.adapter_view(TikTokOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(TikTokOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/trainingpeaks/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/trainingpeaks/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.trainingpeaks.views import ( TrainingPeaksOAuth2Adapter, ) class TrainingPeaksAccount(ProviderAccount): def get_profile_url(self): return "https://app.trainingpeaks.com" def get_avatar_url(self): return None class TrainingPeaksProvider(OAuth2Provider): id = "trainingpeaks" name = "TrainingPeaks" account_class = TrainingPeaksAccount oauth2_adapter_class = TrainingPeaksOAuth2Adapter def extract_uid(self, data): return str(data["Id"]) def extract_common_fields(self, data): extra_common = super().extract_common_fields(data) firstname = data.get("FirstName") lastname = data.get("LastName") # fallback username as there is actually no Username in response username = f"{firstname.strip().lower()}.{lastname.strip().lower()}" name = " ".join(part for part in (firstname, lastname) if part) extra_common.update( username=data.get("username", username), email=data.get("Email"), first_name=firstname, last_name=lastname, name=name.strip(), ) return extra_common def get_default_scope(self): return ["athlete:profile"] provider_classes = [TrainingPeaksProvider] ================================================ FILE: allauth/socialaccount/providers/trainingpeaks/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.trainingpeaks.provider import TrainingPeaksProvider urlpatterns = default_urlpatterns(TrainingPeaksProvider) ================================================ FILE: allauth/socialaccount/providers/trainingpeaks/views.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class TrainingPeaksOAuth2Adapter(OAuth2Adapter): # https://github.com/TrainingPeaks/PartnersAPI/wiki/OAuth provider_id = "trainingpeaks" def get_settings(self): """Provider settings""" return app_settings.PROVIDERS.get(self.provider_id, {}) def get_hostname(self): """Return hostname depending on sandbox setting""" settings = self.get_settings() if settings.get("USE_PRODUCTION"): return "trainingpeaks.com" return "sandbox.trainingpeaks.com" @property def access_token_url(self): return f"https://oauth.{self.get_hostname()}/oauth/token" @property def authorize_url(self): return f"https://oauth.{self.get_hostname()}/OAuth/Authorize" @property def profile_url(self): return f"https://api.{self.get_hostname()}/v1/athlete/profile" @property def api_hostname(self): """Return https://api.hostname.tld""" return f"https://api.{self.get_hostname()}" # https://oauth.sandbox.trainingpeaks.com/oauth/deauthorize scope_delimiter = " " def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: response = sess.get(self.profile_url, headers=headers) response.raise_for_status() extra_data = response.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(TrainingPeaksOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(TrainingPeaksOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/trello/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/trello/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth.provider import OAuthProvider from allauth.socialaccount.providers.trello.views import TrelloOAuthAdapter class TrelloAccount(ProviderAccount): def get_profile_url(self): return None def get_avatar_url(self): return None class TrelloProvider(OAuthProvider): id = "trello" name = "Trello" account_class = TrelloAccount oauth_adapter_class = TrelloOAuthAdapter def get_default_scope(self): return ["read"] def extract_uid(self, data): return data["id"] def extract_common_fields(self, data): return dict( email=data.get("email"), username=data.get("username"), name=data.get("name"), ) def get_auth_params_from_request(self, request, action): data = super().get_auth_params_from_request(request, action) data["type"] = "web_server" data["name"] = self.app.name # define here for how long it will be, this can be configured on the # social app data["expiration"] = "never" return data provider_classes = [TrelloProvider] ================================================ FILE: allauth/socialaccount/providers/trello/urls.py ================================================ from allauth.socialaccount.providers.oauth.urls import default_urlpatterns from allauth.socialaccount.providers.trello.provider import TrelloProvider urlpatterns = default_urlpatterns(TrelloProvider) ================================================ FILE: allauth/socialaccount/providers/trello/views.py ================================================ from django.utils.http import urlencode from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth.views import ( OAuthAdapter, OAuthCallbackView, OAuthLoginView, ) class TrelloOAuthAdapter(OAuthAdapter): provider_id = "trello" request_token_url = "https://trello.com/1/OAuthGetRequestToken" # nosec authorize_url = "https://trello.com/1/OAuthAuthorizeToken" access_token_url = "https://trello.com/1/OAuthGetAccessToken" # nosec def complete_login(self, request, app, token, response): # we need to get the member id and the other information info_url = "{base}?{query}".format( base="https://api.trello.com/1/members/me", query=urlencode({"key": app.key, "token": response.get("oauth_token")}), ) with get_adapter().get_requests_session() as sess: resp = sess.get(info_url) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth_login = OAuthLoginView.adapter_view(TrelloOAuthAdapter) oauth_callback = OAuthCallbackView.adapter_view(TrelloOAuthAdapter) ================================================ FILE: allauth/socialaccount/providers/tumblr/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/tumblr/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth.provider import OAuthProvider from allauth.socialaccount.providers.tumblr.views import TumblrOAuthAdapter class TumblrAccount(ProviderAccount): def get_profile_url_(self): return f"https://{self.account.extra_data.get('name')}.tumblr.com/" class TumblrProvider(OAuthProvider): id = "tumblr" name = "Tumblr" account_class = TumblrAccount oauth_adapter_class = TumblrOAuthAdapter def extract_uid(self, data): return data["name"] def extract_common_fields(self, data): return dict( first_name=data.get("name"), ) provider_classes = [TumblrProvider] ================================================ FILE: allauth/socialaccount/providers/tumblr/urls.py ================================================ from allauth.socialaccount.providers.oauth.urls import default_urlpatterns from allauth.socialaccount.providers.tumblr.provider import TumblrProvider urlpatterns = default_urlpatterns(TumblrProvider) ================================================ FILE: allauth/socialaccount/providers/tumblr/views.py ================================================ from allauth.socialaccount.providers.oauth.client import OAuth from allauth.socialaccount.providers.oauth.views import ( OAuthAdapter, OAuthCallbackView, OAuthLoginView, ) class TumblrAPI(OAuth): url = "https://api.tumblr.com/v2/user/info" def get_user_info(self): data = self.query(self.url).json() return data["response"]["user"] class TumblrOAuthAdapter(OAuthAdapter): provider_id = "tumblr" request_token_url = "https://www.tumblr.com/oauth/request_token" # nosec access_token_url = "https://www.tumblr.com/oauth/access_token" # nosec authorize_url = "https://www.tumblr.com/oauth/authorize" def complete_login(self, request, app, token, response): client = TumblrAPI(request, app.client_id, app.secret, self.request_token_url) extra_data = client.get_user_info() return self.get_provider().sociallogin_from_response(request, extra_data) oauth_login = OAuthLoginView.adapter_view(TumblrOAuthAdapter) oauth_callback = OAuthCallbackView.adapter_view(TumblrOAuthAdapter) ================================================ FILE: allauth/socialaccount/providers/tumblr_oauth2/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/tumblr_oauth2/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.tumblr_oauth2.views import TumblrOAuth2Adapter class TumblrAccount(ProviderAccount): def get_profile_url(self): return f"https://{self.account.extra_data.get('name')}.tumblr.com/" class TumblrOAuth2Provider(OAuth2Provider): id = "tumblr_oauth2" name = "Tumblr" account_class = TumblrAccount oauth2_adapter_class = TumblrOAuth2Adapter def extract_uid(self, data): return data["name"] def extract_common_fields(self, data): return dict( first_name=data.get("name"), ) def get_default_scope(self): return ["read"] provider_classes = [TumblrOAuth2Provider] ================================================ FILE: allauth/socialaccount/providers/tumblr_oauth2/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.tumblr_oauth2.provider import TumblrOAuth2Provider urlpatterns = default_urlpatterns(TumblrOAuth2Provider) ================================================ FILE: allauth/socialaccount/providers/tumblr_oauth2/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class TumblrOAuth2Adapter(OAuth2Adapter): provider_id = "tumblr_oauth2" access_token_url = "https://api.tumblr.com/v2/oauth2/token" # nosec: B105 authorize_url = "https://www.tumblr.com/oauth2/authorize" profile_url = "https://api.tumblr.com/v2/user/info" def complete_login(self, request, app, token, response): extra_data = self.get_user_info(token) return self.get_provider().sociallogin_from_response(request, extra_data) def get_user_info(self, token): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) resp.raise_for_status() return resp.json()["response"]["user"] oauth2_login = OAuth2LoginView.adapter_view(TumblrOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(TumblrOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/twentythreeandme/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/twentythreeandme/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.twentythreeandme.views import ( TwentyThreeAndMeOAuth2Adapter, ) class TwentyThreeAndMeAccount(ProviderAccount): pass class TwentyThreeAndMeProvider(OAuth2Provider): id = "twentythreeandme" slug = "23andme" name = "23andMe" account_class = TwentyThreeAndMeAccount oauth2_adapter_class = TwentyThreeAndMeOAuth2Adapter def extract_uid(self, data): return data["id"] def get_default_scope(self): scope = ["basic"] return scope def extract_common_fields(self, data): return dict( email=data.get("email"), ) provider_classes = [TwentyThreeAndMeProvider] ================================================ FILE: allauth/socialaccount/providers/twentythreeandme/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.twentythreeandme.provider import ( TwentyThreeAndMeProvider, ) urlpatterns = default_urlpatterns(TwentyThreeAndMeProvider) ================================================ FILE: allauth/socialaccount/providers/twentythreeandme/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class TwentyThreeAndMeOAuth2Adapter(OAuth2Adapter): provider_id = "twentythreeandme" access_token_url = "https://api.23andme.com/token" # nosec authorize_url = "https://api.23andme.com/authorize" profile_url = "https://api.23andme.com/1/user/" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(TwentyThreeAndMeOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(TwentyThreeAndMeOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/twitch/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/twitch/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.twitch.views import TwitchOAuth2Adapter class TwitchAccount(ProviderAccount): def get_profile_url(self): return f"https://twitch.tv/{self.account.extra_data.get('login')}" def get_avatar_url(self): # We're using `logo` as a failback for legacy profiles retrieved # with the old https://api.twitch.tv/kraken/user endpoint. logo = self.account.extra_data.get("logo") return self.account.extra_data.get("profile_image_url", logo) def to_str(self): dflt = super().to_str() return self.account.extra_data.get("login", dflt) class TwitchProvider(OAuth2Provider): id = "twitch" name = "Twitch" account_class = TwitchAccount oauth2_adapter_class = TwitchOAuth2Adapter def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return { "username": data.get("login"), "name": data.get("display_name"), "email": data.get("email"), } def get_default_scope(self): return ["user:read:email"] provider_classes = [TwitchProvider] ================================================ FILE: allauth/socialaccount/providers/twitch/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.twitch.provider import TwitchProvider urlpatterns = default_urlpatterns(TwitchProvider) ================================================ FILE: allauth/socialaccount/providers/twitch/views.py ================================================ from http import HTTPStatus from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Error from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class TwitchOAuth2Adapter(OAuth2Adapter): provider_id = "twitch" access_token_url = "https://id.twitch.tv/oauth2/token" # nosec authorize_url = "https://id.twitch.tv/oauth2/authorize" profile_url = "https://api.twitch.tv/helix/users" def complete_login(self, request, app, token, **kwargs): headers = { "Authorization": f"Bearer {token.token}", "Client-ID": app.client_id, } with get_adapter().get_requests_session() as sess: response = sess.get(self.profile_url, headers=headers) data = response.json() if response.status_code >= HTTPStatus.BAD_REQUEST: error = data.get("error", "") message = data.get("message", "") raise OAuth2Error(f"Twitch API Error: {error} ({message})") try: user_info = data.get("data", [])[0] except IndexError: raise OAuth2Error(f"Invalid data from Twitch API: {data}") if "id" not in user_info: raise OAuth2Error(f"Invalid data from Twitch API: {user_info}") return self.get_provider().sociallogin_from_response(request, user_info) oauth2_login = OAuth2LoginView.adapter_view(TwitchOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(TwitchOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/twitter/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/twitter/provider.py ================================================ from allauth.socialaccount.providers.base import AuthAction, ProviderAccount from allauth.socialaccount.providers.oauth.provider import OAuthProvider from allauth.socialaccount.providers.twitter.views import TwitterOAuthAdapter class TwitterAccount(ProviderAccount): def get_screen_name(self): """The screen name is the username of the Twitter account.""" return self.account.extra_data.get("screen_name") def get_profile_url(self): ret = None screen_name = self.get_screen_name() if screen_name: ret = f"https://x.com/{screen_name}" return ret def get_avatar_url(self): ret = None profile_image_url = self.account.extra_data.get("profile_image_url") if profile_image_url: # Hmm, hack to get our hands on the large image. Not # really documented, but seems to work. ret = profile_image_url.replace("_normal", "") return ret def to_str(self): screen_name = self.get_screen_name() return screen_name or super().to_str() class TwitterProvider(OAuthProvider): id = "twitter" name = "X" account_class = TwitterAccount oauth_adapter_class = TwitterOAuthAdapter def get_auth_url(self, request, action): if action == AuthAction.REAUTHENTICATE: url = "https://api.x.com/oauth/authorize" else: url = "https://api.x.com/oauth/authenticate" return url def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): return dict( username=data.get("screen_name"), name=data.get("name"), email=data.get("email"), ) provider_classes = [TwitterProvider] ================================================ FILE: allauth/socialaccount/providers/twitter/urls.py ================================================ from allauth.socialaccount.providers.oauth.urls import default_urlpatterns from allauth.socialaccount.providers.twitter.provider import TwitterProvider urlpatterns = default_urlpatterns(TwitterProvider) ================================================ FILE: allauth/socialaccount/providers/twitter/views.py ================================================ from allauth.socialaccount.app_settings import QUERY_EMAIL from allauth.socialaccount.providers.oauth.client import OAuth from allauth.socialaccount.providers.oauth.views import ( OAuthAdapter, OAuthCallbackView, OAuthLoginView, ) class TwitterAPI(OAuth): """ Verifying twitter credentials """ _base_url = "https://api.x.com/1.1/account/verify_credentials.json" url = f"{_base_url}?include_email=true" if QUERY_EMAIL else _base_url def get_user_info(self): user = self.query(self.url).json() return user class TwitterOAuthAdapter(OAuthAdapter): provider_id = "twitter" request_token_url = "https://api.x.com/oauth/request_token" # nosec access_token_url = "https://api.x.com/oauth/access_token" # nosec # Issue #42 -- this one authenticates over and over again... # authorize_url = 'https://api.twitter.com/oauth/authorize' authorize_url = "https://api.x.com/oauth/authenticate" def complete_login(self, request, app, token, response): client = TwitterAPI(request, app.client_id, app.secret, self.request_token_url) extra_data = client.get_user_info() return self.get_provider().sociallogin_from_response(request, extra_data) oauth_login = OAuthLoginView.adapter_view(TwitterOAuthAdapter) oauth_callback = OAuthCallbackView.adapter_view(TwitterOAuthAdapter) ================================================ FILE: allauth/socialaccount/providers/twitter_oauth2/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/twitter_oauth2/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.twitter_oauth2.views import TwitterOAuth2Adapter class TwitterOAuth2Account(ProviderAccount): def get_username(self): return self.account.extra_data.get("username") def get_profile_url(self): username = self.get_username() if username: return f"https://x.com/{username}" return None def get_avatar_url(self): return self.account.extra_data.get("profile_image_url") class TwitterOAuth2Provider(OAuth2Provider): id = "twitter_oauth2" name = "X" account_class = TwitterOAuth2Account oauth2_adapter_class = TwitterOAuth2Adapter pkce_enabled_default = True def extract_uid(self, data): return data["id"] def extract_common_fields(self, data): return dict( name=data["name"], username=data["username"], ) def get_fields(self): settings = self.get_settings() default_fields = [ "id", "name", "username", "verified", "profile_image_url", "created_at", ] return settings.get("FIELDS", default_fields) def get_default_scope(self): return ["users.read", "tweet.read"] provider_classes = [TwitterOAuth2Provider] ================================================ FILE: allauth/socialaccount/providers/twitter_oauth2/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.twitter_oauth2.provider import ( TwitterOAuth2Provider, ) urlpatterns = default_urlpatterns(TwitterOAuth2Provider) ================================================ FILE: allauth/socialaccount/providers/twitter_oauth2/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class TwitterOAuth2Adapter(OAuth2Adapter): provider_id = "twitter_oauth2" access_token_url = "https://api.x.com/2/oauth2/token" # nosec authorize_url = "https://x.com/i/oauth2/authorize" profile_url = "https://api.x.com/2/users/me" basic_auth = True def complete_login(self, request, app, access_token, **kwargs): extra_data = self.get_user_info(access_token) return self.get_provider().sociallogin_from_response(request, extra_data) def get_user_info(self, token): fields = self.get_provider().get_fields() headers = { **self.get_provider().get_settings().get("HEADERS", {}), "Authorization": f"Bearer {token.token}", } with get_adapter().get_requests_session() as sess: resp = sess.get( url=self.profile_url, params={"user.fields": ",".join(fields)}, headers=headers, ) resp.raise_for_status() return resp.json()["data"] oauth2_login = OAuth2LoginView.adapter_view(TwitterOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(TwitterOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/untappd/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/untappd/client.py ================================================ from http import HTTPStatus from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Client, OAuth2Error class UntappdOAuth2Client(OAuth2Client): """ Custom client because Untappd: * uses redirect_url instead of redirect_uri * nests access_token inside an extra 'response' object """ def get_access_token(self, code, pkce_code_verifier=None): from allauth.socialaccount.providers.untappd.provider import UntappdProvider data = { "client_id": self.consumer_key, "redirect_url": self.callback_url, "grant_type": "authorization_code", "response_type": "code", "client_secret": self.consumer_secret, "code": code, } params = None self._strip_empty_keys(data) url = self.access_token_url if self.access_token_method == "GET": # nosec params = data data = None if data and pkce_code_verifier: data["code_verifier"] = pkce_code_verifier # Allow custom User Agent to comply with Untappd API settings = app_settings.PROVIDERS.get(UntappdProvider.id, {}) headers = {"User-Agent": settings.get("USER_AGENT", "django-allauth")} # TODO: Proper exception handling with get_adapter().get_requests_session() as sess: resp = sess.request( self.access_token_method, url, params=params, data=data, headers=headers, ) access_token = None if resp.status_code == HTTPStatus.OK: access_token = resp.json()["response"] if not access_token or "access_token" not in access_token: raise OAuth2Error(f"Error retrieving access token: {resp.content}") return access_token ================================================ FILE: allauth/socialaccount/providers/untappd/provider.py ================================================ from django.urls import reverse from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.untappd.views import UntappdOAuth2Adapter class UntappdAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("untappd_url") def get_avatar_url(self): return self.account.extra_data.get("user_avatar") def get_user_data(self): return self.account.extra_data.get("response", {}).get("user", {}) class UntappdProvider(OAuth2Provider): id = "untappd" name = "Untappd" account_class = UntappdAccount oauth2_adapter_class = UntappdOAuth2Adapter def get_auth_params_from_request(self, request, action): params = super().get_auth_params_from_request(request, action) # Untappd uses redirect_url instead of redirect_uri params["redirect_url"] = request.build_absolute_uri( reverse(f"{self.id}_callback") ) return params def extract_uid(self, data): return str(data["response"]["user"]["uid"]) def extract_common_fields(self, data): user = data["response"]["user"] return dict( username=user["user_name"], name=f"{user['first_name']} {user['last_name']}", ) def extract_email_addresses(self, data): ret = [ EmailAddress( email=data["response"]["user"]["settings"]["email_address"], verified=True, primary=True, ) ] return ret provider_classes = [UntappdProvider] ================================================ FILE: allauth/socialaccount/providers/untappd/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.untappd.provider import UntappdProvider urlpatterns = default_urlpatterns(UntappdProvider) ================================================ FILE: allauth/socialaccount/providers/untappd/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) from .client import UntappdOAuth2Client class UntappdOAuth2Adapter(OAuth2Adapter): client_class = UntappdOAuth2Client provider_id = "untappd" access_token_url = "https://untappd.com/oauth/authorize/" # nosec access_token_method = "GET" # nosec authorize_url = "https://untappd.com/oauth/authenticate/" user_info_url = "https://api.untappd.com/v4/user/info/" def complete_login(self, request, app, token, **kwargs): with get_adapter().get_requests_session() as sess: resp = sess.get(self.user_info_url, params={"access_token": token.token}) extra_data = resp.json() # TODO: get and store the email from the user info json return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(UntappdOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(UntappdOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/vimeo/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/vimeo/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth.provider import OAuthProvider from allauth.socialaccount.providers.vimeo.views import VimeoOAuthAdapter class VimeoAccount(ProviderAccount): pass class VimeoProvider(OAuthProvider): id = "vimeo" name = "Vimeo" account_class = VimeoAccount oauth_adapter_class = VimeoOAuthAdapter def get_default_scope(self): scope = [] return scope def extract_uid(self, data): return data["id"] def extract_common_fields(self, data): return dict(name=data.get("display_name"), username=data.get("username")) provider_classes = [VimeoProvider] ================================================ FILE: allauth/socialaccount/providers/vimeo/urls.py ================================================ from allauth.socialaccount.providers.oauth.urls import default_urlpatterns from allauth.socialaccount.providers.vimeo.provider import VimeoProvider urlpatterns = default_urlpatterns(VimeoProvider) ================================================ FILE: allauth/socialaccount/providers/vimeo/views.py ================================================ from allauth.socialaccount.providers.oauth.client import OAuth from allauth.socialaccount.providers.oauth.views import ( OAuthAdapter, OAuthCallbackView, OAuthLoginView, ) class VimeoAPI(OAuth): url = "https://vimeo.com/api/rest/v2?method=vimeo.people.getInfo" def get_user_info(self): url = self.url data = self.query(url, params=dict(format="json")).json() return data["person"] class VimeoOAuthAdapter(OAuthAdapter): provider_id = "vimeo" request_token_url = "https://vimeo.com/oauth/request_token" # nosec access_token_url = "https://vimeo.com/oauth/access_token" # nosec authorize_url = "https://vimeo.com/oauth/authorize" def complete_login(self, request, app, token, response): client = VimeoAPI(request, app.client_id, app.secret, self.request_token_url) extra_data = client.get_user_info() return self.get_provider().sociallogin_from_response(request, extra_data) oauth_login = OAuthLoginView.adapter_view(VimeoOAuthAdapter) oauth_callback = OAuthCallbackView.adapter_view(VimeoOAuthAdapter) ================================================ FILE: allauth/socialaccount/providers/vimeo_oauth2/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/vimeo_oauth2/models.py ================================================ ================================================ FILE: allauth/socialaccount/providers/vimeo_oauth2/provider.py ================================================ """ Provider for Patreon """ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.vimeo_oauth2.views import VimeoOAuth2Adapter class VimeoOAuth2Account(ProviderAccount): pass class VimeoOAuth2Provider(OAuth2Provider): id = "vimeo_oauth2" name = "Vimeo" account_class = VimeoOAuth2Account oauth2_adapter_class = VimeoOAuth2Adapter def get_default_scope(self): return ["public", "private"] def extract_uid(self, data): return data.get("uri").split("/")[-1] def extract_common_fields(self, data): return { "fullname": data.get("name"), } provider_classes = [VimeoOAuth2Provider] ================================================ FILE: allauth/socialaccount/providers/vimeo_oauth2/urls.py ================================================ """URLs for Patreon Provider""" from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.vimeo_oauth2.provider import VimeoOAuth2Provider urlpatterns = default_urlpatterns(VimeoOAuth2Provider) ================================================ FILE: allauth/socialaccount/providers/vimeo_oauth2/views.py ================================================ """ Views for PatreonProvider https://www.patreon.com/platform/documentation/oauth """ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class VimeoOAuth2Adapter(OAuth2Adapter): provider_id = "vimeo_oauth2" access_token_url = "https://api.vimeo.com/oauth/access_token" # nosec authorize_url = "https://api.vimeo.com/oauth/authorize" profile_url = "https://api.vimeo.com/me/" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(VimeoOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(VimeoOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/vk/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/vk/provider.py ================================================ from allauth.socialaccount import app_settings from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.vk.views import VKOAuth2Adapter class VKAccount(ProviderAccount): def get_profile_url(self): return f"https://vk.ru/id{self.account.extra_data.get('id')}" def get_avatar_url(self): ret = None photo_big_url = self.account.extra_data.get("photo_big") photo_medium_url = self.account.extra_data.get("photo_medium") if photo_big_url: return photo_big_url elif photo_medium_url: return photo_medium_url else: return ret class VKProvider(OAuth2Provider): id = "vk" name = "VK" account_class = VKAccount oauth2_adapter_class = VKOAuth2Adapter pkce_enabled_default = True def get_default_scope(self): scope = [] if app_settings.QUERY_EMAIL: scope.append("email") return scope def extract_uid(self, data): return str(data["user_id"]) def extract_common_fields(self, data): return dict( email=data.get("email"), last_name=data.get("last_name"), username=data.get("screen_name"), first_name=data.get("first_name"), phone=data.get("phone"), ) provider_classes = [VKProvider] ================================================ FILE: allauth/socialaccount/providers/vk/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.vk.provider import VKProvider urlpatterns = default_urlpatterns(VKProvider) ================================================ FILE: allauth/socialaccount/providers/vk/views.py ================================================ import uuid from requests import RequestException from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) from allauth.utils import get_request_param class VKOAuth2Adapter(OAuth2Adapter): provider_id = "vk" access_token_url = "https://id.vk.ru/oauth2/auth" # nosec authorize_url = "https://id.vk.ru/authorize" profile_url = "https://id.vk.ru/oauth2/user_info" def get_access_token_data(self, request, app, client, pkce_code_verifier=None): code = get_request_param(self.request, "code") device_id = get_request_param(self.request, "device_id") extra_data = { # "state" isn't strictly necessary for now, but since VK ID documentation doesn't # specify required vs optional parameters at all, we still add this (now optional) # param to make the code less fragile for future, when they may start requiring it "state": str(uuid.uuid4()), "device_id": device_id, } data = client.get_access_token( code, pkce_code_verifier=pkce_code_verifier, extra_data=extra_data ) self.did_fetch_access_token = True return data def complete_login(self, request, app, token, **kwargs): req_data = { "access_token": token.token, "client_id": app.client_id, } with get_adapter().get_requests_session() as sess: resp = sess.post(self.profile_url, data=req_data) resp.raise_for_status() resp_data = resp.json() if "error" in resp_data or "user" not in resp_data: raise RequestException( "Could not get basic data for user being authenticated" ) return self.get_provider().sociallogin_from_response(request, resp_data["user"]) oauth2_login = OAuth2LoginView.adapter_view(VKOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(VKOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/wahoo/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/wahoo/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount import app_settings from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.wahoo.views import WahooOAuth2Adapter class WahooAccount(ProviderAccount): def get_profile_url(self): return "https://api.wahooligan.com/v1/user" class WahooProvider(OAuth2Provider): id = "wahoo" name = "Wahoo" account_class = WahooAccount oauth2_adapter_class = WahooOAuth2Adapter def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): extra_common = super().extract_common_fields(data) extra_common.update( # Additional properties we ignore: gender, created_at, updated_at height=data.get("height"), weight=data.get("weight"), first=data.get("first"), last=data.get("last"), birth=data.get("birth"), ) return extra_common def extract_email_addresses(self, data): email = EmailAddress( email=data.get("email"), primary=True, verified=False, ) return [email] def get_default_scope(self): scope = ["user_read"] if app_settings.QUERY_EMAIL: scope.append("email") return scope provider_classes = [WahooProvider] ================================================ FILE: allauth/socialaccount/providers/wahoo/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.wahoo.provider import WahooProvider urlpatterns = default_urlpatterns(WahooProvider) ================================================ FILE: allauth/socialaccount/providers/wahoo/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class WahooOAuth2Adapter(OAuth2Adapter): provider_id = "wahoo" access_token_url = "https://api.wahooligan.com/oauth/token" # nosec authorize_url = "https://api.wahooligan.com/oauth/authorize" profile_url = "https://api.wahooligan.com/v1/user" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(WahooOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(WahooOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/weibo/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/weibo/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount, ProviderException from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.weibo.views import WeiboOAuth2Adapter class WeiboAccount(ProviderAccount): def get_profile_url(self): # profile_url = "u/3195025850" return f"https://www.weibo.com/{self.account.extra_data.get('profile_url')}" def get_avatar_url(self): return self.account.extra_data.get("avatar_large") class WeiboProvider(OAuth2Provider): id = "weibo" name = "Weibo" account_class = WeiboAccount oauth2_adapter_class = WeiboOAuth2Adapter def extract_uid(self, data): ret = data.get("idstr") if not ret: raise ProviderException("Missing 'idstr'") return ret def extract_common_fields(self, data): return dict(username=data.get("screen_name"), name=data.get("name")) provider_classes = [WeiboProvider] ================================================ FILE: allauth/socialaccount/providers/weibo/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.weibo.provider import WeiboProvider urlpatterns = default_urlpatterns(WeiboProvider) ================================================ FILE: allauth/socialaccount/providers/weibo/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class WeiboOAuth2Adapter(OAuth2Adapter): provider_id = "weibo" access_token_url = "https://api.weibo.com/oauth2/access_token" # nosec authorize_url = "https://api.weibo.com/oauth2/authorize" profile_url = "https://api.weibo.com/2/users/show.json" def complete_login(self, request, app, token, **kwargs): uid = kwargs.get("response", {}).get("uid") with get_adapter().get_requests_session() as sess: params = {"access_token": token.token, "uid": uid} resp = sess.get(self.profile_url, params=params) extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(WeiboOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(WeiboOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/weixin/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/weixin/client.py ================================================ from collections import OrderedDict from http import HTTPStatus from django.utils.http import urlencode from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.client import OAuth2Client, OAuth2Error class WeixinOAuth2Client(OAuth2Client): def get_redirect_url(self, authorization_url, scope, extra_params): scope = self.scope_delimiter.join(set(scope)) params = { "appid": self.consumer_key, "redirect_uri": self.callback_url, "scope": scope, "response_type": "code", } if self.state: params["state"] = self.state params.update(extra_params) sorted_params = OrderedDict() for param in sorted(params): sorted_params[param] = params[param] return f"{authorization_url}?{urlencode(sorted_params)}" def get_access_token(self, code, pkce_code_verifier=None): data = { "appid": self.consumer_key, "grant_type": "authorization_code", "secret": self.consumer_secret, "code": code, } params = None self._strip_empty_keys(data) url = self.access_token_url if self.access_token_method == "GET": # nosec params = data data = None if data and pkce_code_verifier: data["code_verifier"] = pkce_code_verifier # TODO: Proper exception handling with get_adapter().get_requests_session() as sess: resp = sess.request(self.access_token_method, url, params=params, data=data) access_token = None if resp.status_code == HTTPStatus.OK: access_token = resp.json() if not access_token or "access_token" not in access_token: raise OAuth2Error(f"Error retrieving access token: {resp.content}") return access_token ================================================ FILE: allauth/socialaccount/providers/weixin/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.weixin.views import WeixinOAuth2Adapter class WeixinAccount(ProviderAccount): def get_avatar_url(self): return self.account.extra_data.get("headimgurl") def to_str(self): return self.account.extra_data.get("nickname", super().to_str()) class WeixinProvider(OAuth2Provider): id = "weixin" name = "Weixin" account_class = WeixinAccount oauth2_adapter_class = WeixinOAuth2Adapter def extract_uid(self, data): return data["openid"] def get_default_scope(self): return ["snsapi_login"] def extract_common_fields(self, data): return dict(username=data.get("nickname"), name=data.get("nickname")) provider_classes = [WeixinProvider] ================================================ FILE: allauth/socialaccount/providers/weixin/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.weixin.provider import WeixinProvider urlpatterns = default_urlpatterns(WeixinProvider) ================================================ FILE: allauth/socialaccount/providers/weixin/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) from .client import WeixinOAuth2Client class WeixinOAuth2Adapter(OAuth2Adapter): provider_id = "weixin" access_token_url = "https://api.weixin.qq.com/sns/oauth2/access_token" # nosec profile_url = "https://api.weixin.qq.com/sns/userinfo" client_class = WeixinOAuth2Client @property def authorize_url(self): settings = self.get_provider().get_settings() url = settings.get( "AUTHORIZE_URL", "https://open.weixin.qq.com/connect/qrconnect" ) return url def complete_login(self, request, app, token, **kwargs): openid = kwargs.get("response", {}).get("openid") with get_adapter().get_requests_session() as sess: params = {"access_token": token.token, "openid": openid} resp = sess.get(self.profile_url, params=params) resp.raise_for_status() extra_data = resp.json() nickname = extra_data.get("nickname") if nickname: extra_data["nickname"] = nickname.encode("raw_unicode_escape").decode( "utf-8" ) return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(WeixinOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(WeixinOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/windowslive/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/windowslive/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.windowslive.views import WindowsLiveOAuth2Adapter class WindowsLiveAccount(ProviderAccount): def to_str(self): email = self.account.extra_data.get("emails", {}).get("preferred") if email: return email return super().to_str() class WindowsLiveProvider(OAuth2Provider): id = "windowslive" name = "Live" account_class = WindowsLiveAccount oauth2_adapter_class = WindowsLiveOAuth2Adapter def get_default_scope(self): return ["wl.basic", "wl.emails"] def extract_uid(self, data): return str(data["id"]) def extract_common_fields(self, data): try: email = data.get("emails").get("preferred") except AttributeError: email = None return dict( email=email, last_name=data.get("last_name"), first_name=data.get("first_name"), ) provider_classes = [WindowsLiveProvider] ================================================ FILE: allauth/socialaccount/providers/windowslive/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.windowslive.provider import WindowsLiveProvider urlpatterns = default_urlpatterns(WindowsLiveProvider) ================================================ FILE: allauth/socialaccount/providers/windowslive/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class WindowsLiveOAuth2Adapter(OAuth2Adapter): provider_id = "windowslive" access_token_url = "https://login.live.com/oauth20_token.srf" # nosec authorize_url = "https://login.live.com/oauth20_authorize.srf" profile_url = "https://apis.live.net/v5.0/me" def complete_login(self, request, app, token, **kwargs): # example of what's returned (in python format): # {'first_name': 'James', 'last_name': 'Smith', # 'name': 'James Smith', 'locale': 'en_US', 'gender': None, # 'emails': {'personal': None, 'account': 'jsmith@example.com', # 'business': None, 'preferred': 'jsmith@example.com'}, # 'link': 'https://profile.live.com/', # 'updated_time': '2014-02-07T00:35:27+0000', # 'id': '83605e110af6ff98'} with get_adapter().get_requests_session() as sess: headers = {"Authorization": f"Bearer {token.token}"} resp = sess.get(self.profile_url, headers=headers) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(WindowsLiveOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(WindowsLiveOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/xing/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/xing/provider.py ================================================ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth.provider import OAuthProvider from allauth.socialaccount.providers.xing.views import XingOAuthAdapter class XingAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("permalink") def get_avatar_url(self): return self.account.extra_data.get("photo_urls", {}).get("large") def to_str(self): dflt = super().to_str() return self.account.extra_data.get("active_email") or dflt class XingProvider(OAuthProvider): id = "xing" name = "Xing" account_class = XingAccount oauth_adapter_class = XingOAuthAdapter def extract_uid(self, data): return data["id"] def extract_common_fields(self, data): return dict( email=data.get("active_email"), username=data.get("page_name"), first_name=data.get("first_name"), last_name=data.get("last_name"), ) provider_classes = [XingProvider] ================================================ FILE: allauth/socialaccount/providers/xing/urls.py ================================================ from allauth.socialaccount.providers.oauth.urls import default_urlpatterns from allauth.socialaccount.providers.xing.provider import XingProvider urlpatterns = default_urlpatterns(XingProvider) ================================================ FILE: allauth/socialaccount/providers/xing/views.py ================================================ from allauth.socialaccount.providers.oauth.client import OAuth from allauth.socialaccount.providers.oauth.views import ( OAuthAdapter, OAuthCallbackView, OAuthLoginView, ) class XingAPI(OAuth): url = "https://api.xing.com/v1/users/me.json" def get_user_info(self): user = self.query(self.url).json() return user class XingOAuthAdapter(OAuthAdapter): provider_id = "xing" request_token_url = "https://api.xing.com/v1/request_token" # nosec access_token_url = "https://api.xing.com/v1/access_token" # nosec authorize_url = "https://www.xing.com/v1/authorize" def complete_login(self, request, app, token, response): client = XingAPI(request, app.client_id, app.secret, self.request_token_url) extra_data = client.get_user_info()["users"][0] return self.get_provider().sociallogin_from_response(request, extra_data) oauth_login = OAuthLoginView.adapter_view(XingOAuthAdapter) oauth_callback = OAuthCallbackView.adapter_view(XingOAuthAdapter) ================================================ FILE: allauth/socialaccount/providers/yahoo/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/yahoo/models.py ================================================ # Create your models here. ================================================ FILE: allauth/socialaccount/providers/yahoo/provider.py ================================================ from allauth.socialaccount.models import EmailAddress from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.yahoo.views import YahooOAuth2Adapter class YahooAccount(ProviderAccount): pass class YahooProvider(OAuth2Provider): id = "yahoo" name = "Yahoo" account_class = YahooAccount oauth2_adapter_class = YahooOAuth2Adapter def get_default_scope(self): """ Doc on scopes available at https://developer.yahoo.com/oauth2/guide/yahoo_scopes/ """ return ["profile", "email"] def extract_uid(self, data): return data["sub"] def extract_common_fields(self, data): return dict( email=data["email"], last_name=data["family_name"], first_name=data["given_name"], ) def extract_email_addresses(self, data): ret = [] email = data.get("email") if email and data.get("email_verified"): ret.append(EmailAddress(email=email, verified=True, primary=True)) return ret provider_classes = [YahooProvider] ================================================ FILE: allauth/socialaccount/providers/yahoo/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.yahoo.provider import YahooProvider urlpatterns = default_urlpatterns(YahooProvider) ================================================ FILE: allauth/socialaccount/providers/yahoo/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class YahooOAuth2Adapter(OAuth2Adapter): provider_id = "yahoo" access_token_url = "https://api.login.yahoo.com/oauth2/get_token" # nosec authorize_url = "https://api.login.yahoo.com/oauth2/request_auth" profile_url = "https://api.login.yahoo.com/openid/v1/userinfo" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(YahooOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(YahooOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/yandex/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/yandex/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount import app_settings from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.yandex.views import YandexOAuth2Adapter class YandexAccount(ProviderAccount): def to_str(self): email = self.account.extra_data.get("default_email") if email: return email return super().to_str() class YandexProvider(OAuth2Provider): id = "yandex" name = "Yandex" account_class = YandexAccount oauth2_adapter_class = YandexOAuth2Adapter def get_default_scope(self): scope = ["login:info"] if app_settings.QUERY_EMAIL: scope.append("login:email") return scope def extract_uid(self, data): return str(data["id"]) def get_user_email(self, data): email = data.get("default_email") if not email: emails = data.get("emails") email = emails[0] if emails else "" return email def extract_common_fields(self, data): email = self.get_user_email(data) return dict( email=email, last_name=data.get("last_name"), username=data.get("display_name"), first_name=data.get("first_name"), ) def extract_email_addresses(self, data): ret = [] email = self.get_user_email(data) if email: ret.append(EmailAddress(email=email, verified=True, primary=True)) return ret provider_classes = [YandexProvider] ================================================ FILE: allauth/socialaccount/providers/yandex/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.yandex.provider import YandexProvider urlpatterns = default_urlpatterns(YandexProvider) ================================================ FILE: allauth/socialaccount/providers/yandex/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class YandexOAuth2Adapter(OAuth2Adapter): provider_id = "yandex" access_token_url = "https://oauth.yandex.ru/token" # nosec authorize_url = "https://oauth.yandex.com/authorize" profile_url = "https://login.yandex.ru/info" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"OAuth {token.token}"} with get_adapter().get_requests_session() as sess: params = {"format": "json"} resp = sess.get(self.profile_url, params=params, headers=headers) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(YandexOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(YandexOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/ynab/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/ynab/provider.py ================================================ from allauth.socialaccount.providers.base import AuthAction, ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.ynab.views import YNABOAuth2Adapter class Scope: ACCESS = "read-only" class YNABAccount(ProviderAccount): pass class YNABProvider(OAuth2Provider): id = "ynab" name = "YNAB" account_class = YNABAccount oauth2_adapter_class = YNABOAuth2Adapter def get_default_scope(self): scope = [Scope.ACCESS] return scope def get_auth_params_from_request(self, request, action): ret = super().get_auth_params_from_request(request, action) if action == AuthAction.REAUTHENTICATE: ret["prompt"] = "select_account consent" return ret def extract_uid(self, data): return str(data["data"]["user"]["id"]) provider_classes = [YNABProvider] ================================================ FILE: allauth/socialaccount/providers/ynab/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.ynab.provider import YNABProvider urlpatterns = default_urlpatterns(YNABProvider) ================================================ FILE: allauth/socialaccount/providers/ynab/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class YNABOAuth2Adapter(OAuth2Adapter): provider_id = "ynab" access_token_url = "https://app.youneedabudget.com/oauth/token" # nosec authorize_url = "https://app.youneedabudget.com/oauth/authorize" profile_url = "https://api.youneedabudget.com/v1/user" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(YNABOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(YNABOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/zoho/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/zoho/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.zoho.views import ZohoOAuth2Adapter class ZohoAccount(ProviderAccount): pass class ZohoProvider(OAuth2Provider): id = "zoho" name = "Zoho" account_class = ZohoAccount oauth2_adapter_class = ZohoOAuth2Adapter def get_default_scope(self): return ["aaaserver.profile.READ"] def extract_uid(self, data): return str(data["ZUID"]) def extract_common_fields(self, data): return dict( email=data["Email"], username=data["Display_Name"], first_name=data["First_Name"], last_name=data["Last_Name"], ) def extract_email_addresses(self, data): ret = [] email = data.get("Email") if email: ret.append( EmailAddress( email=email, verified=False, primary=True, ) ) return ret provider_classes = [ZohoProvider] ================================================ FILE: allauth/socialaccount/providers/zoho/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.zoho.provider import ZohoProvider urlpatterns = default_urlpatterns(ZohoProvider) ================================================ FILE: allauth/socialaccount/providers/zoho/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class ZohoOAuth2Adapter(OAuth2Adapter): provider_id = "zoho" access_token_url = "https://accounts.zoho.com/oauth/v2/token" # nosec authorize_url = "https://accounts.zoho.com/oauth/v2/auth" profile_url = "https://accounts.zoho.com/oauth/user/info" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(ZohoOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(ZohoOAuth2Adapter) ================================================ FILE: allauth/socialaccount/providers/zoom/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/providers/zoom/provider.py ================================================ from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider from allauth.socialaccount.providers.zoom.views import ZoomOAuth2Adapter class ZoomAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get("vanity_url") def get_avatar_url(self): return self.account.extra_data.get("pic_url") class ZoomProvider(OAuth2Provider): id = "zoom" name = "Zoom" account_class = ZoomAccount oauth2_adapter_class = ZoomOAuth2Adapter def extract_uid(self, data): return data["id"] def extract_common_fields(self, data): return dict( email=data["email"], first_name=data["first_name"], last_name=data["last_name"], ) def extract_email_addresses(self, data): ret = [] email = data.get("email") if email and data.get("verified"): ret.append(EmailAddress(email=email, verified=True, primary=True)) return ret provider_classes = [ZoomProvider] ================================================ FILE: allauth/socialaccount/providers/zoom/urls.py ================================================ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns from allauth.socialaccount.providers.zoom.provider import ZoomProvider urlpatterns = default_urlpatterns(ZoomProvider) ================================================ FILE: allauth/socialaccount/providers/zoom/views.py ================================================ from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.providers.oauth2.views import ( OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView, ) class ZoomOAuth2Adapter(OAuth2Adapter): provider_id = "zoom" access_token_url = "https://zoom.us/oauth/token" # nosec authorize_url = "https://zoom.us/oauth/authorize" profile_url = "https://api.zoom.us/v2/users/me" def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": f"Bearer {token.token}"} with get_adapter().get_requests_session() as sess: resp = sess.get(self.profile_url, headers=headers) resp.raise_for_status() extra_data = resp.json() return self.get_provider().sociallogin_from_response(request, extra_data) oauth2_login = OAuth2LoginView.adapter_view(ZoomOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(ZoomOAuth2Adapter) ================================================ FILE: allauth/socialaccount/sessions.py ================================================ from importlib import import_module from urllib.parse import urlparse from django.conf import settings from django.utils.cache import patch_vary_headers engine = import_module(settings.SESSION_ENGINE) SessionStore = engine.SessionStore class LoginSession: """Some providers sometimes POST their responses, which due to CORS/Samesite-cookie rules means that this request cannot access the session as its session cookie is unavailable. We work around this by storing the response in a separate, temporary session and redirecting to a different endpoint that can pick up the flow. """ def __init__(self, request, attribute_name, cookie_name): """ Prepares an provider specific session. """ self.request = request self.attribute_name = attribute_name self.cookie_name = cookie_name self.store = getattr(request, attribute_name, None) if self.store is None: session_key = request.COOKIES.get(cookie_name) self.store = SessionStore(session_key) setattr(request, attribute_name, self.store) def save(self, response): """ Save the session and set a cookie. """ patch_vary_headers(response, ("Cookie",)) self.store.save() kwargs = {} samesite = getattr(settings, "SESSION_COOKIE_SAMESITE", None) if samesite: kwargs["samesite"] = samesite response.set_cookie( self.cookie_name, self.store.session_key, max_age=None, expires=None, domain=settings.SESSION_COOKIE_DOMAIN, # The cookie is only needed on this endpoint path=urlparse(response.url).path, secure=settings.SESSION_COOKIE_SECURE, httponly=None, **kwargs, ) def delete(self): self.store.delete() ================================================ FILE: allauth/socialaccount/signals.py ================================================ from django.dispatch import Signal # Sent after a user successfully authenticates via a social provider, # but before the login is actually processed. This signal is emitted # for social logins, signups and when connecting additional social # accounts to an account. # Provides the arguments "request", "sociallogin" pre_social_login = Signal() # Sent after a user connects a social account to a their local account. # Provides the arguments "request", "sociallogin" social_account_added = Signal() # Sent after a user connects an already existing social account to a # their local account. The social account will have an updated token and # refreshed extra_data. # Provides the arguments "request", "sociallogin" social_account_updated = Signal() # Sent after a user disconnects a social account from their local # account. # Provides the arguments "request", "socialaccount" social_account_removed = Signal() ================================================ FILE: allauth/socialaccount/templatetags/__init__.py ================================================ ================================================ FILE: allauth/socialaccount/templatetags/socialaccount.py ================================================ from django import template from django.contrib.auth import REDIRECT_FIELD_NAME from django.utils.safestring import mark_safe from allauth.socialaccount.adapter import get_adapter from allauth.utils import get_request_param register = template.Library() @register.simple_tag(takes_context=True) def provider_login_url(context, provider, **params): """ {% provider_login_url "facebook" next=bla %} {% provider_login_url "openid" openid="https://me.yahoo.com" next=bla %} """ request = context.get("request") if isinstance(provider, str): adapter = get_adapter() provider = adapter.get_provider(request, provider) query = dict(params) auth_params = query.get("auth_params", None) scope = query.get("scope", None) process = query.get("process", None) if scope == "": del query["scope"] if auth_params == "": del query["auth_params"] if REDIRECT_FIELD_NAME not in query: next = get_request_param(request, REDIRECT_FIELD_NAME) if next: query[REDIRECT_FIELD_NAME] = next elif process == "redirect": query[REDIRECT_FIELD_NAME] = request.get_full_path() else: if not query[REDIRECT_FIELD_NAME]: del query[REDIRECT_FIELD_NAME] # get the login url and append query as url parameters return provider.get_login_url(request, **query) @register.simple_tag(takes_context=True) def providers_media_js(context): request = context["request"] providers = get_adapter().list_providers(request) ret = "\n".join(p.media_js(request) for p in providers) return mark_safe(ret) # nosec @register.simple_tag def get_social_accounts(user): """ {% get_social_accounts user as accounts %} Then: {{accounts.twitter}} -- a list of connected Twitter accounts {{accounts.twitter.0}} -- the first Twitter account {% if accounts %} -- if there is at least one social account """ accounts = {} for account in user.socialaccount_set.all().iterator(): providers = accounts.setdefault(account.provider, []) providers.append(account) return accounts @register.simple_tag(takes_context=True) def get_providers(context): """ Returns a list of social authentication providers. Usage: `{% get_providers as socialaccount_providers %}`. Then within the template context, `socialaccount_providers` will hold a list of social providers configured for the current site. """ request = context["request"] adapter = get_adapter() providers = adapter.list_providers(request) providers = [ provider for provider in providers if (not provider.uses_apps or not provider.app.settings.get("hidden")) ] return sorted(providers, key=lambda p: p.name) ================================================ FILE: allauth/socialaccount/urls.py ================================================ from django.urls import path from . import views urlpatterns = [ path( "login/cancelled/", views.login_cancelled, name="socialaccount_login_cancelled", ), path("login/error/", views.login_error, name="socialaccount_login_error"), path("signup/", views.signup, name="socialaccount_signup"), path("", views.connections, name="socialaccount_connections"), ] ================================================ FILE: allauth/socialaccount/views.py ================================================ from http import HTTPStatus from django.contrib.auth.decorators import login_required from django.contrib.sites.shortcuts import get_current_site from django.http import HttpResponse, HttpResponseRedirect from django.urls import reverse, reverse_lazy from django.utils.decorators import method_decorator from django.views.generic.base import TemplateView from django.views.generic.edit import FormView from allauth.account.internal.decorators import login_not_required from allauth.socialaccount.forms import DisconnectForm, SignupForm from allauth.socialaccount.internal import flows from allauth.socialaccount.models import SocialAccount from ..account import app_settings as account_settings from ..account.views import ( AjaxCapableProcessFormViewMixin, CloseableSignupMixin, RedirectAuthenticatedUserMixin, ) from ..utils import get_form_class from . import app_settings from .adapter import get_adapter class SignupView( RedirectAuthenticatedUserMixin, CloseableSignupMixin, AjaxCapableProcessFormViewMixin, FormView, ): form_class = SignupForm template_name = f"socialaccount/signup.{account_settings.TEMPLATE_EXTENSION}" def get_form_class(self): return get_form_class(app_settings.FORMS, "signup", self.form_class) @method_decorator(login_not_required) def dispatch(self, request, *args, **kwargs) -> HttpResponse: self.sociallogin = flows.signup.get_pending_signup(request) if not self.sociallogin: return HttpResponseRedirect(reverse("account_login")) return super().dispatch(request, *args, **kwargs) def is_open(self) -> bool: return get_adapter(self.request).is_open_for_signup( self.request, self.sociallogin ) def get_form_kwargs(self) -> dict: ret = super().get_form_kwargs() ret["sociallogin"] = self.sociallogin return ret def form_valid(self, form) -> HttpResponse: return flows.signup.signup_by_form(self.request, self.sociallogin, form) def get_context_data(self, **kwargs): ret = super().get_context_data(**kwargs) ret.update( dict( site=get_current_site(self.request), account=self.sociallogin.account, ) ) return ret def get_authenticated_redirect_url(self) -> str: return reverse("socialaccount_connections") signup = SignupView.as_view() @method_decorator(login_not_required, name="dispatch") class LoginCancelledView(TemplateView): template_name = ( f"socialaccount/login_cancelled.{account_settings.TEMPLATE_EXTENSION}" ) login_cancelled = LoginCancelledView.as_view() class LoginErrorView(TemplateView): template_name = ( f"socialaccount/authentication_error.{account_settings.TEMPLATE_EXTENSION}" ) def get(self, request, *args, **kwargs) -> HttpResponse: return self.render_to_response( self.get_context_data(**kwargs), status=HTTPStatus.UNAUTHORIZED, ) login_error = LoginErrorView.as_view() @method_decorator(login_required, name="dispatch") class ConnectionsView(AjaxCapableProcessFormViewMixin, FormView): template_name = f"socialaccount/connections.{account_settings.TEMPLATE_EXTENSION}" form_class = DisconnectForm success_url = reverse_lazy("socialaccount_connections") def get_form_class(self): return get_form_class(app_settings.FORMS, "disconnect", self.form_class) def get_form_kwargs(self) -> dict: kwargs = super().get_form_kwargs() kwargs["request"] = self.request return kwargs def form_valid(self, form) -> HttpResponse: form.save() return super().form_valid(form) def get_ajax_data(self): account_data = [] for account in SocialAccount.objects.filter(user=self.request.user): provider_account = account.get_provider_account() account_data.append( { "id": account.pk, "provider": account.provider, "name": provider_account.to_str(), } ) return {"socialaccounts": account_data} connections = ConnectionsView.as_view() ================================================ FILE: allauth/templates/account/account_inactive.html ================================================ {% extends "allauth/layouts/entrance.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% translate "Account Inactive" %} {% endblock head_title %} {% block content %} {% element h1 %} {% translate "Account Inactive" %} {% endelement %} {% element p %} {% translate "This account is inactive." %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/account/base_confirm_code.html ================================================ {% extends "account/base_entrance.html" %} {% load i18n %} {% load allauth account %} {% block content %} {% setvar title_ %} {% block title %}{% endblock %} {% endsetvar %} {% setvar action_url %} {% block action_url %}{% endblock %} {% endsetvar %} {% setvar extra_tags %} {% block extra_tags %}{% endblock %} {% endsetvar %} {% setvar form_tags %} entrance,{{ extra_tags }} {% endsetvar %} {% setvar submit_button_tags %} confirm,{{ extra_tags }} {% endsetvar %} {% setvar recipient %} {% block recipient %}{% endblock %} {% endsetvar %} {% element h1 %} {{ title_ }} {% endelement %} {% element p %} {% blocktranslate %}We've sent a code to {{ recipient }}. The code expires shortly, so please enter it soon.{% endblocktranslate %} {% endelement %} {% element form form=verify_form method="post" action=action_url tags=form_tags %} {% slot body %} {% csrf_token %} {% element fields form=verify_form unlabeled=True %} {% endelement %} {{ redirect_field }} {% endslot %} {% slot actions %} {% element button_group %} {% element button type="submit" tags=submit_button_tags %} {% translate "Confirm" %} {% endelement %} {% if can_resend %} {% element button form="resend" tags="outline,resend" %} {% translate "Request new code" %} {% endelement %} {% endif %} {% if cancel_url %} {% element button href=cancel_url tags="outline,cancel" %} {% translate "Cancel" %} {% endelement %} {% else %} {% element button type="submit" form="logout-from-stage" tags="outline,cancel" %} {% translate "Cancel" %} {% endelement %} {% endif %} {% endelement %} {% endslot %} {% endelement %}
{{ redirect_field }} {% csrf_token %}
{% if not cancel_url %}
{% csrf_token %}
{% endif %} {% setvar summary_ %} {% block change_title %}{% endblock %} {% endsetvar %} {% if can_change %} {% element details open=change_form.errors %} {% slot summary %} {{ summary_ }} {% endslot %} {% slot body %} {% element form form=change_form method="post" action=action_url %} {% slot body %} {% csrf_token %} {% element fields form=change_form unlabeled=True %} {% endelement %} {{ redirect_field }} {% endslot %} {% slot actions %} {% element button name="action" value="change" type="submit" %} Change {% endelement %} {% endslot %} {% endelement %} {% endslot %} {% endelement %} {% endif %} {% endblock content %} ================================================ FILE: allauth/templates/account/base_entrance.html ================================================ {% extends "allauth/layouts/entrance.html" %} ================================================ FILE: allauth/templates/account/base_manage.html ================================================ {% extends "allauth/layouts/manage.html" %} ================================================ FILE: allauth/templates/account/base_manage_email.html ================================================ {% extends "account/base_manage.html" %} ================================================ FILE: allauth/templates/account/base_manage_password.html ================================================ {% extends "account/base_manage.html" %} ================================================ FILE: allauth/templates/account/base_manage_phone.html ================================================ {% extends "account/base_manage.html" %} ================================================ FILE: allauth/templates/account/base_reauthenticate.html ================================================ {% extends "account/base_entrance.html" %} {% load allauth %} {% load i18n %} {% block head_title %} {% trans "Confirm Access" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Confirm Access" %} {% endelement %} {% element p %} {% blocktranslate %}Please reauthenticate to safeguard your account.{% endblocktranslate %} {% endelement %} {% block reauthenticate_content %}{% endblock %} {% if reauthentication_alternatives %} {% element hr %} {% endelement %} {% element h2 %} {% translate "Alternative options" %} {% endelement %} {% element button_group %} {% for alt in reauthentication_alternatives %} {% element button href=alt.url tags="primary,outline" %} {{ alt.description }} {% endelement %} {% endfor %} {% endelement %} {% endif %} {% endblock content %} ================================================ FILE: allauth/templates/account/confirm_email_verification_code.html ================================================ {% extends "account/base_confirm_code.html" %} {% load i18n %} {% load allauth account %} {% block head_title %} {% translate "Email Verification" %} {% endblock head_title %} {% block title %} {% translate "Enter Email Verification Code" %} {% endblock %} {% block recipient %}{{ email }}{% endblock %} {% block action_url %} {% url 'account_email_verification_sent' %} {% endblock %} {% block extra_tags %}email,verification{% endblock %} {% block change_title %} {% translate "Use a different email address" %} {% endblock %} ================================================ FILE: allauth/templates/account/confirm_login_code.html ================================================ {% extends "account/base_confirm_code.html" %} {% load i18n %} {% load allauth account %} {% block head_title %} {% translate "Sign In" %} {% endblock head_title %} {% block title %} {% translate "Enter Sign-In Code" %} {% endblock %} {% block recipient %} {% if email %} {{ email }} {% else %} {{ phone }} {% endif %} {% endblock %} {% block action_url %} {% url 'account_confirm_login_code' %} {% endblock %} {% block extra_tags %}login{% endblock %} ================================================ FILE: allauth/templates/account/confirm_password_reset_code.html ================================================ {% extends "account/base_confirm_code.html" %} {% load i18n %} {% load allauth account %} {% block head_title %} {% translate "Password Reset" %} {% endblock head_title %} {% block title %} {% translate "Enter Password Reset Code" %} {% endblock %} {% block recipient %}{{ email }}{% endblock %} {% block action_url %} {% url 'account_confirm_password_reset_code' %} {% endblock %} {% block extra_tags %}email,verification{% endblock %} ================================================ FILE: allauth/templates/account/confirm_phone_verification_code.html ================================================ {% extends "account/base_confirm_code.html" %} {% load i18n %} {% load allauth account %} {% block head_title %} {% translate "Phone Verification" %} {% endblock head_title %} {% block title %} {% translate "Enter Phone Verification Code" %} {% endblock %} {% block recipient %}{{ phone }}{% endblock %} {% block action_url %} {% url 'account_verify_phone' %} {% endblock %} {% block change_title %} {% translate "Use a different phone number" %} {% endblock %} {% block extra_tags %}phone,verification{% endblock %} ================================================ FILE: allauth/templates/account/email/account_already_exists_message.txt ================================================ {% extends "account/email/base_message.txt" %} {% load i18n %} {% block content %}{% autoescape off %}{% blocktrans %}You are receiving this email because you or someone else tried to signup for an account using email address: {{ email }} However, an account using that email address already exists. In case you have forgotten about this, please use the password forgotten procedure to recover your account: {{ password_reset_url }}{% endblocktrans %}{% endautoescape %}{% endblock content %} ================================================ FILE: allauth/templates/account/email/account_already_exists_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Account Already Exists{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/account/email/base_message.txt ================================================ {% load i18n %}{% autoescape off %}{% blocktrans with site_name=current_site.name %}Hello from {{ site_name }}!{% endblocktrans %} {% block content %}{% endblock content %} {% blocktrans with site_name=current_site.name site_domain=current_site.domain %}Thank you for using {{ site_name }}! {{ site_domain }}{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/account/email/base_notification.txt ================================================ {% extends "account/email/base_message.txt" %} {% load account %} {% load i18n %} {% block content %}{% autoescape off %}{% blocktrans %}You are receiving this mail because the following change was made to your account:{% endblocktrans %} {% block notification_message %} {% endblock notification_message%} {% blocktrans %}If you do not recognize this change then please take proper security precautions immediately. The change to your account originates from: - IP address: {{ip}} - Browser: {{user_agent}} - Date: {{timestamp}}{% endblocktrans %}{% endautoescape %}{% endblock %} ================================================ FILE: allauth/templates/account/email/email_changed_message.txt ================================================ {% extends "account/email/base_notification.txt" %} {% load i18n %} {% block notification_message %}{% blocktrans %}Your email has been changed from {{ from_email }} to {{ to_email }}.{% endblocktrans %}{% endblock notification_message %} ================================================ FILE: allauth/templates/account/email/email_changed_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Email Changed{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/account/email/email_confirm_message.txt ================================================ {% extends "account/email/base_notification.txt" %} {% load i18n %} {% block notification_message %}{% blocktrans %}Your email has been confirmed.{% endblocktrans %}{% endblock notification_message %} ================================================ FILE: allauth/templates/account/email/email_confirm_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Email Confirmation{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/account/email/email_confirmation_message.txt ================================================ {% extends "account/email/base_message.txt" %} {% load account %} {% load i18n %} {% block content %}{% autoescape off %}{% user_display user as user_display %}{% blocktranslate with site_name=current_site.name site_domain=current_site.domain %}You're receiving this email because user {{ user_display }} has given your email address to register an account on {{ site_domain }}.{% endblocktranslate %} {% if code %}{% blocktranslate %}Your email verification code is listed below. Please enter it in your open browser window.{% endblocktranslate %} {{ code }}{% else %}{% blocktranslate %}To confirm this is correct, go to {{ activate_url }}{% endblocktranslate %}{% endif %}{% endautoescape %}{% endblock content %} ================================================ FILE: allauth/templates/account/email/email_confirmation_signup_message.txt ================================================ {% include "account/email/email_confirmation_message.txt" %} ================================================ FILE: allauth/templates/account/email/email_confirmation_signup_subject.txt ================================================ {% include "account/email/email_confirmation_subject.txt" %} ================================================ FILE: allauth/templates/account/email/email_confirmation_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Please Confirm Your Email Address{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/account/email/email_deleted_message.txt ================================================ {% extends "account/email/base_notification.txt" %} {% load i18n %} {% block notification_message %}{% blocktrans %}Email address {{ deleted_email }} has been removed from your account.{% endblocktrans %}{% endblock notification_message %} ================================================ FILE: allauth/templates/account/email/email_deleted_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Email Removed{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/account/email/login_code_message.txt ================================================ {% extends "account/email/base_message.txt" %} {% load account %} {% load i18n %} {% block content %}{% autoescape off %}{% blocktranslate %}Your sign-in code is listed below. Please enter it in your open browser window.{% endblocktranslate %}{% endautoescape %} {{ code }} {% blocktranslate %}This mail can be safely ignored if you did not initiate this action.{% endblocktranslate %}{% endblock content %} ================================================ FILE: allauth/templates/account/email/login_code_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Sign-In Code{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/account/email/password_changed_message.txt ================================================ {% extends "account/email/base_notification.txt" %} {% load i18n %} {% block notification_message %}{% blocktrans %}Your password has been changed.{% endblocktrans %}{% endblock notification_message %} ================================================ FILE: allauth/templates/account/email/password_changed_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Password Changed{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/account/email/password_reset_code_message.txt ================================================ {% extends "account/email/base_message.txt" %} {% load account %} {% load i18n %} {% block content %}{% autoescape off %}{% blocktranslate %}Your password reset code is listed below. Please enter it in your open browser window.{% endblocktranslate %}{% endautoescape %} {{ code }} {% blocktranslate %}This mail can be safely ignored if you did not initiate this action.{% endblocktranslate %}{% endblock content %} ================================================ FILE: allauth/templates/account/email/password_reset_code_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Password Reset Code{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/account/email/password_reset_key_message.txt ================================================ {% extends "account/email/base_message.txt" %} {% load i18n %} {% block content %}{% autoescape off %}{% blocktrans %}You're receiving this email because you or someone else has requested a password reset for your user account. It can be safely ignored if you did not request a password reset. Click the link below to reset your password.{% endblocktrans %} {{ password_reset_url }}{% if username %} {% blocktrans %}In case you forgot, your username is {{ username }}.{% endblocktrans %}{% endif %}{% endautoescape %}{% endblock content %} ================================================ FILE: allauth/templates/account/email/password_reset_key_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Password Reset Email{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/account/email/password_reset_message.txt ================================================ {% extends "account/email/base_notification.txt" %} {% load i18n %} {% block notification_message %}{% blocktrans %}Your password has been reset.{% endblocktrans %}{% endblock notification_message %} ================================================ FILE: allauth/templates/account/email/password_reset_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Password Reset{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/account/email/password_set_message.txt ================================================ {% extends "account/email/base_notification.txt" %} {% load i18n %} {% block notification_message %}{% blocktrans %}Your password has been set.{% endblocktrans %}{% endblock notification_message %} ================================================ FILE: allauth/templates/account/email/password_set_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Password Set{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/account/email/unknown_account_message.txt ================================================ {% extends "account/email/base_message.txt" %} {% load i18n %} {% block content %}{% autoescape off %}{% blocktranslate %}You are receiving this email because you, or someone else, tried to access an account with email {{ email }}. However, we do not have any record of such an account in our database.{% endblocktranslate %} {% blocktranslate %}This mail can be safely ignored if you did not initiate this action.{% endblocktranslate %} {% blocktranslate %}If it was you, you can sign up for an account using the link below.{% endblocktranslate %} {{ signup_url }}{% endautoescape %}{% endblock content %} ================================================ FILE: allauth/templates/account/email/unknown_account_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Unknown Account{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/account/email.html ================================================ {% extends "account/base_manage_email.html" %} {% load static allauth i18n %} {% block head_title %} {% trans "Email Addresses" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Email Addresses" %} {% endelement %} {% if emailaddresses %} {% element p %} {% trans 'The following email addresses are associated with your account:' %} {% endelement %} {% url 'account_email' as email_url %} {% element form form=form action=email_url method="post" tags="email,list" %} {% slot body %} {% csrf_token %} {% for radio in emailaddress_radios %} {% with emailaddress=radio.emailaddress %} {% element field type="radio" checked=radio.checked name="email" value=emailaddress.email id=radio.id %} {% slot label %} {{ emailaddress.email }} {% if emailaddress.verified %} {% element badge tags="success,email,verified" %} {% translate "Verified" %} {% endelement %} {% else %} {% element badge tags="warning,email,unverified" %} {% translate "Unverified" %} {% endelement %} {% endif %} {% if emailaddress.primary %} {% element badge tags="email,primary" %} {% translate "Primary" %} {% endelement %} {% endif %} {% endslot %} {% endelement %} {% endwith %} {% endfor %} {% endslot %} {% slot actions %} {% element button type="submit" name="action_primary" %} {% trans 'Make Primary' %} {% endelement %} {% element button tags="secondary" type="submit" name="action_send" %} {% trans 'Re-send Verification' %} {% endelement %} {% element button tags="danger,delete" type="submit" name="action_remove" %} {% trans 'Remove' %} {% endelement %} {% endslot %} {% endelement %} {% else %} {% include "account/snippets/warn_no_email.html" %} {% endif %} {% if can_add_email %} {% element h2 %} {% trans "Add Email Address" %} {% endelement %} {% url 'account_email' as action_url %} {% element form form=form method="post" action=action_url tags="email,add" %} {% slot body %} {% csrf_token %} {% element fields form=form %} {% endelement %} {% endslot %} {% slot actions %} {% element button name="action_add" type="submit" %} {% trans "Add Email" %} {% endelement %} {% endslot %} {% endelement %} {% endif %} {% endblock content %} {% block extra_body %} {% endblock extra_body %} ================================================ FILE: allauth/templates/account/email_change.html ================================================ {% extends "account/base_manage_email.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Email Address" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Email Address" %} {% endelement %} {% if not emailaddresses %} {% include "account/snippets/warn_no_email.html" %} {% endif %} {% url 'account_email' as action_url %} {% element form method="post" action=action_url %} {% slot body %} {% csrf_token %} {% if current_emailaddress %} {% element field id="current_email" disabled=True type="email" value=current_emailaddress.email %} {% slot label %} {% translate "Current email" %}: {% endslot %} {% endelement %} {% endif %} {% if new_emailaddress %} {% element field id="new_email" value=new_emailaddress.email disabled=True type="email" %} {% slot label %} {% if not current_emailaddress %} {% translate "Current email" %}: {% else %} {% translate "Changing to" %}: {% endif %} {% endslot %} {% slot help_text %} {% blocktranslate %}Your email address is still pending verification.{% endblocktranslate %} {% element button form="pending-email" type="submit" name="action_send" tags="minor,secondary" %} {% trans 'Re-send Verification' %} {% endelement %} {% if current_emailaddress %} {% element button form="pending-email" type="submit" name="action_remove" tags="danger,minor" %} {% trans 'Cancel Change' %} {% endelement %} {% endif %} {% endslot %} {% endelement %} {% endif %} {% element field id=form.email.auto_id name="email" value=form.email.value errors=form.email.errors type="email" %} {% slot label %} {% translate "Change to" %}: {% endslot %} {% endelement %} {% endslot %} {% slot actions %} {% element button name="action_add" type="submit" %} {% trans "Change Email" %} {% endelement %} {% endslot %} {% endelement %} {% if new_emailaddress %} {% endif %} {% endblock content %} ================================================ FILE: allauth/templates/account/email_confirm.html ================================================ {% extends "account/base_entrance.html" %} {% load i18n %} {% load account %} {% load allauth %} {% block head_title %} {% trans "Confirm Email Address" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Confirm Email Address" %} {% endelement %} {% if confirmation %} {% user_display confirmation.email_address.user as user_display %} {% if can_confirm %} {% element p %} {% blocktrans with confirmation.email_address.email as email %}Please confirm that {{ email }} is an email address for user {{ user_display }}.{% endblocktrans %} {% endelement %} {% url 'account_confirm_email' confirmation.key as action_url %} {% element form method="post" action=action_url %} {% slot actions %} {% csrf_token %} {{ redirect_field }} {% element button type="submit" %} {% trans 'Confirm' %} {% endelement %} {% endslot %} {% endelement %} {% else %} {% element p %} {% blocktrans %}Unable to confirm {{ email }} because it is already confirmed by a different account.{% endblocktrans %} {% endelement %} {% endif %} {% else %} {% url 'account_email' as email_url %} {% element p %} {% blocktrans %}This email confirmation link expired or is invalid. Please issue a new email confirmation request.{% endblocktrans %} {% endelement %} {% endif %} {% endblock content %} ================================================ FILE: allauth/templates/account/login.html ================================================ {% extends "account/base_entrance.html" %} {% load i18n %} {% load allauth account %} {% block head_title %} {% trans "Sign In" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Sign In" %} {% endelement %} {% if not SOCIALACCOUNT_ONLY %} {% setvar link %} {% endsetvar %} {% setvar end_link %} {% endsetvar %} {% element p %} {% blocktranslate %}If you have not created an account yet, then please {{ link }}sign up{{ end_link }} first.{% endblocktranslate %} {% endelement %} {% url 'account_login' as login_url %} {% element form form=form method="post" action=login_url tags="entrance,login" %} {% slot body %} {% csrf_token %} {% element fields form=form unlabeled=True %} {% endelement %} {{ redirect_field }} {% endslot %} {% slot actions %} {% element button type="submit" tags="prominent,login" %} {% trans "Sign In" %} {% endelement %} {% endslot %} {% endelement %} {% endif %} {% if LOGIN_BY_CODE_ENABLED or PASSKEY_LOGIN_ENABLED %} {% element hr %} {% endelement %} {% element button_group vertical=True %} {% if PASSKEY_LOGIN_ENABLED %} {% element button type="submit" form="mfa_login" id="passkey_login" tags="prominent,login,outline,primary" %} {% trans "Sign in with a passkey" %} {% endelement %} {% endif %} {% if LOGIN_BY_CODE_ENABLED %} {% element button href=request_login_code_url tags="prominent,login,outline,primary" %} {% trans "Send me a sign-in code" %} {% endelement %} {% endif %} {% endelement %} {% endif %} {% if SOCIALACCOUNT_ENABLED %} {% include "socialaccount/snippets/login.html" with page_layout="entrance" %} {% endif %} {% endblock content %} {% block extra_body %} {{ block.super }} {% if PASSKEY_LOGIN_ENABLED %} {% include "mfa/webauthn/snippets/login_script.html" with button_id="passkey_login" %} {% endif %} {% endblock %} ================================================ FILE: allauth/templates/account/logout.html ================================================ {% extends "account/base_manage.html" %} {% load allauth i18n %} {% block head_title %} {% trans "Sign Out" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Sign Out" %} {% endelement %} {% element p %} {% trans 'Are you sure you want to sign out?' %} {% endelement %} {% url 'account_logout' as action_url %} {% element form method="post" action=action_url no_visible_fields=True %} {% slot body %} {% csrf_token %} {{ redirect_field }} {% endslot %} {% slot actions %} {% element button type="submit" %} {% trans 'Sign Out' %} {% endelement %} {% endslot %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/account/messages/cannot_delete_primary_email.txt ================================================ {% load i18n %} {% blocktrans %}You cannot remove your primary email address ({{email}}).{% endblocktrans %} ================================================ FILE: allauth/templates/account/messages/email_confirmation_failed.txt ================================================ {% load i18n %} {% blocktrans %}Unable to confirm {{email}} because it is already confirmed by a different account.{% endblocktrans %} ================================================ FILE: allauth/templates/account/messages/email_confirmation_sent.txt ================================================ {% load i18n %} {% blocktrans %}Confirmation email sent to {{email}}.{% endblocktrans %} ================================================ FILE: allauth/templates/account/messages/email_confirmed.txt ================================================ {% load i18n %} {% blocktrans %}You have confirmed {{email}}.{% endblocktrans %} ================================================ FILE: allauth/templates/account/messages/email_deleted.txt ================================================ {% load i18n %} {% blocktrans %}Removed email address {{email}}.{% endblocktrans %} ================================================ FILE: allauth/templates/account/messages/logged_in.txt ================================================ {% load account %} {% load i18n %} {% user_display user as name %} {% blocktrans %}Successfully signed in as {{name}}.{% endblocktrans %} ================================================ FILE: allauth/templates/account/messages/logged_out.txt ================================================ {% load i18n %} {% blocktrans %}You have signed out.{% endblocktrans %} ================================================ FILE: allauth/templates/account/messages/login_code_sent.txt ================================================ {% load i18n %} {% blocktrans %}A sign-in code has been sent to {{recipient}}.{% endblocktrans %} ================================================ FILE: allauth/templates/account/messages/password_changed.txt ================================================ {% load i18n %} {% blocktrans %}Password successfully changed.{% endblocktrans %} ================================================ FILE: allauth/templates/account/messages/password_set.txt ================================================ {% load i18n %} {% blocktrans %}Password successfully set.{% endblocktrans %} ================================================ FILE: allauth/templates/account/messages/phone_verification_sent.txt ================================================ {% load i18n %} {% blocktrans %}A verification code has been sent to {{phone}}.{% endblocktrans %} ================================================ FILE: allauth/templates/account/messages/phone_verified.txt ================================================ {% load i18n %} {% blocktrans %}You have verified phone number {{phone}}.{% endblocktrans %} ================================================ FILE: allauth/templates/account/messages/primary_email_set.txt ================================================ {% load i18n %} {% blocktrans %}Primary email address set.{% endblocktrans %} ================================================ FILE: allauth/templates/account/messages/unverified_primary_email.txt ================================================ {% load i18n %} {% blocktrans %}Your primary email address must be verified.{% endblocktrans %} ================================================ FILE: allauth/templates/account/password_change.html ================================================ {% extends "account/base_manage_password.html" %} {% load allauth i18n %} {% block head_title %} {% trans "Change Password" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Change Password" %} {% endelement %} {% url 'account_change_password' as action_url %} {% element form form=form method="post" action=action_url %} {% slot body %} {% csrf_token %} {{ redirect_field }} {% element fields form=form %} {% endelement %} {% endslot %} {% slot actions %} {% element button type="submit" %} {% trans "Change Password" %} {% endelement %} {% trans "Forgot Password?" %} {% endslot %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/account/password_reset.html ================================================ {% extends "account/base_entrance.html" %} {% load i18n allauth account %} {% block head_title %} {% trans "Password Reset" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Password Reset" %} {% endelement %} {% if user.is_authenticated %} {% include "account/snippets/already_logged_in.html" %} {% endif %} {% element p %} {% trans "Forgotten your password? Enter your email address below, and we'll send you an email allowing you to reset it." %} {% endelement %} {% url 'account_reset_password' as reset_url %} {% element form form=form method="post" action=reset_url %} {% slot body %} {% csrf_token %} {% element fields form=form %} {% endelement %} {{ redirect_field }} {% endslot %} {% slot actions %} {% element button type="submit" %} {% trans 'Reset My Password' %} {% endelement %} {% endslot %} {% endelement %} {% element p %} {% blocktrans %}Please contact us if you have any trouble resetting your password.{% endblocktrans %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/account/password_reset_done.html ================================================ {% extends "account/base_entrance.html" %} {% load i18n %} {% load allauth %} {% load account %} {% block head_title %} {% trans "Password Reset" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Password Reset" %} {% endelement %} {% if user.is_authenticated %} {% include "account/snippets/already_logged_in.html" %} {% endif %} {% element p %} {% blocktrans %}We have sent you an email. If you have not received it please check your spam folder. Otherwise contact us if you do not receive it in a few minutes.{% endblocktrans %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/account/password_reset_from_key.html ================================================ {% extends "account/base_entrance.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Change Password" %} {% endblock head_title %} {% block content %} {% element h1 %} {% if token_fail %} {% trans "Bad Token" %} {% else %} {% trans "Change Password" %} {% endif %} {% endelement %} {% if token_fail %} {% url 'account_reset_password' as passwd_reset_url %} {% element p %} {% blocktrans %}The password reset link was invalid, possibly because it has already been used. Please request a new password reset.{% endblocktrans %} {% endelement %} {% else %} {% element form method="post" action=action_url %} {% slot body %} {% csrf_token %} {{ redirect_field }} {% element fields form=form %} {% endelement %} {% endslot %} {% slot actions %} {% element button type="submit" name="action" %} {% trans 'Change Password' %} {% endelement %} {% element button type="submit" form="logout-from-stage" tags="link,cancel" %} {% translate "Cancel" %} {% endelement %} {% endslot %} {% endelement %} {% endif %} {% if not cancel_url %}
{% csrf_token %}
{% endif %} {% endblock content %} ================================================ FILE: allauth/templates/account/password_reset_from_key_done.html ================================================ {% extends "account/base_entrance.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Change Password" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Change Password" %} {% endelement %} {% element p %} {% trans 'Your password is now changed.' %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/account/password_set.html ================================================ {% extends "account/base_manage_password.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Set Password" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Set Password" %} {% endelement %} {% url 'account_set_password' as action_url %} {% element form method="post" action=action_url %} {% slot body %} {% csrf_token %} {{ redirect_field }} {% element fields form=form %} {% endelement %} {% endslot %} {% slot actions %} {% element button type="submit" name="action" %} {% trans 'Set Password' %} {% endelement %} {% endslot %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/account/phone_change.html ================================================ {% extends "account/base_manage_phone.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Change Phone" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Change Phone" %} {% endelement %} {% url 'account_change_phone' as action_url %} {% element form method="post" action=action_url %} {% slot body %} {% csrf_token %} {% if phone %} {% element field id="current_phone" disabled=True type="tel" value=phone %} {% slot label %} {% translate "Current phone" %}: {% endslot %} {% if not phone_verified %} {% slot help_text %} {% blocktranslate %}Your phone number is still pending verification.{% endblocktranslate %} {% element button form="verify-phone" type="submit" tags="minor,secondary" %} {% trans 'Re-send Verification' %} {% endelement %} {% endslot %} {% endif %} {% endelement %} {% endif %} {% element field id=form.phone.auto_id name="phone" value=form.phone.value errors=form.phone.errors type="tel" %} {% slot label %} {% translate "Change to" %}: {% endslot %} {% endelement %} {% endslot %} {% slot actions %} {% element button name="action_add" type="submit" %} {% translate "Change Phone" context "action" %} {% endelement %} {% endslot %} {% endelement %} {% if not phone_verified %} {% endif %} {% endblock content %} ================================================ FILE: allauth/templates/account/reauthenticate.html ================================================ {% extends "account/base_reauthenticate.html" %} {% load allauth %} {% load i18n %} {% block reauthenticate_content %} {% element p %} {% blocktranslate %}Enter your password:{% endblocktranslate %} {% endelement %} {% url 'account_reauthenticate' as action_url %} {% element form form=form method="post" action=action_url %} {% slot body %} {% csrf_token %} {% element fields form=form unlabeled=True %} {% endelement %} {{ redirect_field }} {% endslot %} {% slot actions %} {% element button type="submit" tags="primary,reauthenticate" %} {% trans "Confirm" %} {% endelement %} {% endslot %} {% endelement %} {% endblock %} ================================================ FILE: allauth/templates/account/request_login_code.html ================================================ {% extends "account/base_entrance.html" %} {% load i18n %} {% load allauth account %} {% block head_title %} {% translate "Sign In" %} {% endblock head_title %} {% block content %} {% element h1 %} {% translate "Send me a sign-in code" %} {% endelement %} {% element p %} {% blocktranslate %}You will receive a special code for a password-free sign-in.{% endblocktranslate %} {% endelement %} {% url 'account_request_login_code' as login_url %} {% element form form=form method="post" action=login_url tags="entrance,login" %} {% slot body %} {% csrf_token %} {% element fields form=form unlabeled=True %} {% endelement %} {{ redirect_field }} {% endslot %} {% slot actions %} {% element button type="submit" tags="prominent,login" %} {% translate "Request Code" %} {% endelement %} {% endslot %} {% endelement %} {% url 'account_login' as login_url %} {% element button href=login_url tags="link" %} {% translate "Other sign-in options" %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/account/signup.html ================================================ {% extends "account/base_entrance.html" %} {% load allauth i18n %} {% block head_title %} {% trans "Signup" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Sign Up" %} {% endelement %} {% setvar link %} {% endsetvar %} {% setvar end_link %} {% endsetvar %} {% element p %} {% blocktranslate %}Already have an account? Then please {{ link }}sign in{{ end_link }}.{% endblocktranslate %} {% endelement %} {% if not SOCIALACCOUNT_ONLY %} {% url 'account_signup' as action_url %} {% element form form=form method="post" action=action_url tags="entrance,signup" %} {% slot body %} {% csrf_token %} {% element fields form=form unlabeled=True %} {% endelement %} {{ redirect_field }} {% endslot %} {% slot actions %} {% element button tags="prominent,signup" type="submit" %} {% trans "Sign Up" %} {% endelement %} {% endslot %} {% endelement %} {% endif %} {% if PASSKEY_SIGNUP_ENABLED %} {% element hr %} {% endelement %} {% element button href=signup_by_passkey_url tags="prominent,signup,outline,primary" %} {% trans "Sign up using a passkey" %} {% endelement %} {% endif %} {% if SOCIALACCOUNT_ENABLED %} {% include "socialaccount/snippets/login.html" with page_layout="entrance" %} {% endif %} {% endblock content %} ================================================ FILE: allauth/templates/account/signup_by_passkey.html ================================================ {% extends "account/base_entrance.html" %} {% load allauth i18n %} {% block head_title %} {% trans "Signup" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Passkey Sign Up" %} {% endelement %} {% setvar link %} {% endsetvar %} {% setvar end_link %} {% endsetvar %} {% element p %} {% blocktranslate %}Already have an account? Then please {{ link }}sign in{{ end_link }}.{% endblocktranslate %} {% endelement %} {% url 'account_signup_by_passkey' as action_url %} {% element form form=form method="post" action=action_url tags="entrance,signup" %} {% slot body %} {% csrf_token %} {% element fields form=form unlabeled=True %} {% endelement %} {{ redirect_field }} {% endslot %} {% slot actions %} {% element button tags="prominent,signup" type="submit" %} {% trans "Sign Up" %} {% endelement %} {% endslot %} {% endelement %} {% element hr %} {% endelement %} {% element button href=signup_url tags="prominent,signup,outline,primary" %} {% trans "Other options" %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/account/signup_closed.html ================================================ {% extends "account/base_entrance.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Sign Up Closed" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Sign Up Closed" %} {% endelement %} {% element p %} {% trans "We are sorry, but the sign up is currently closed." %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/account/snippets/already_logged_in.html ================================================ {% load i18n %} {% load account %} {% load allauth %} {% user_display user as user_display %} {% element alert %} {% slot message %} {% blocktranslate %}Note{% endblocktranslate %}: {% blocktranslate %}You are already logged in as {{ user_display }}.{% endblocktranslate %} {% endslot %} {% endelement %} ================================================ FILE: allauth/templates/account/snippets/warn_no_email.html ================================================ {% load i18n allauth %} {% element p %} {% trans 'Warning:' %} {% trans "You currently do not have any email address set up. You should really add an email address so you can receive notifications, reset your password, etc." %} {% endelement %} ================================================ FILE: allauth/templates/account/verification_sent.html ================================================ {% extends "account/base_entrance.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Verify Your Email Address" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Verify Your Email Address" %} {% endelement %} {% element p %} {% blocktrans %}We have sent an email to you for verification. Follow the link provided to finalize the signup process. If you do not see the verification email in your main inbox, check your spam folder. Please contact us if you do not receive the verification email within a few minutes.{% endblocktrans %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/account/verified_email_required.html ================================================ {% extends "account/base_manage.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Verify Your Email Address" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Verify Your Email Address" %} {% endelement %} {% url 'account_email' as email_url %} {% element p %} {% blocktrans %}This part of the site requires us to verify that you are who you claim to be. For this purpose, we require that you verify ownership of your email address. {% endblocktrans %} {% endelement %} {% element p %} {% blocktrans %}We have sent an email to you for verification. Please click on the link inside that email. If you do not see the verification email in your main inbox, check your spam folder. Otherwise contact us if you do not receive it within a few minutes.{% endblocktrans %} {% endelement %} {% element p %} {% blocktrans %}Note: you can still change your email address.{% endblocktrans %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/allauth/elements/alert.html ================================================ {% load allauth %}

{% slot message %} {% endslot %}

================================================ FILE: allauth/templates/allauth/elements/badge.html ================================================ {% load allauth %} {% slot %} {% endslot %} ================================================ FILE: allauth/templates/allauth/elements/button.html ================================================ {% load allauth %} {% comment %} djlint:off {% endcomment %} <{% if attrs.href %}a href="{{ attrs.href }}"{% else %}button{% endif %} {% if attrs.form %}form="{{ attrs.form }}"{% endif %} {% if attrs.id %}id="{{ attrs.id }}"{% endif %} {% if attrs.name %}name="{{ attrs.name }}"{% endif %} {% if attrs.value %}value="{{ attrs.value }}"{% endif %} {% if attrs.type %}type="{{ attrs.type }}"{% endif %} > {% slot %} {% endslot %} ================================================ FILE: allauth/templates/allauth/elements/button_group.html ================================================ {% load allauth %}
{% slot %} {% endslot %}
================================================ FILE: allauth/templates/allauth/elements/details.html ================================================ {% load allauth %}
{% slot summary %} {% endslot %} {% slot body %} {% endslot %}
================================================ FILE: allauth/templates/allauth/elements/field.html ================================================ {% load allauth %} {{ attrs.errors }}

{% if attrs.type == "textarea" %} {% else %} {% if attrs.type != "checkbox" and attrs.type != "radio" %} {% endif %} {% if attrs.type == "checkbox" or attrs.type == "radio" %} {% endif %} {% endif %} {% if slots.help_text %} {% slot help_text %} {% endslot %} {% endif %}

================================================ FILE: allauth/templates/allauth/elements/fields.html ================================================ {{ attrs.form.as_p }} ================================================ FILE: allauth/templates/allauth/elements/form.html ================================================ {% load allauth %}
{% slot body %} {% endslot %} {% slot actions %} {% endslot %}
================================================ FILE: allauth/templates/allauth/elements/h1.html ================================================ {% comment %} djlint:off {% endcomment %}{% load allauth %}

{% slot %}{% endslot %}

================================================ FILE: allauth/templates/allauth/elements/h2.html ================================================ {% comment %} djlint:off {% endcomment %}{% load allauth %}

{% slot %}{% endslot %}

================================================ FILE: allauth/templates/allauth/elements/hr.html ================================================
================================================ FILE: allauth/templates/allauth/elements/img.html ================================================ ================================================ FILE: allauth/templates/allauth/elements/p.html ================================================ {% comment %} djlint:off {% endcomment %}{% load allauth %}

{% slot %}{% endslot %}

================================================ FILE: allauth/templates/allauth/elements/panel.html ================================================ {% load allauth %}

{% slot title %} {% endslot %}

{% slot body %} {% endslot %} {% if slots.actions %}
    {% for action in slots.actions %}
  • {{ action }}
  • {% endfor %}
{% endif %}
================================================ FILE: allauth/templates/allauth/elements/provider.html ================================================
  • {{ attrs.name }}
  • ================================================ FILE: allauth/templates/allauth/elements/provider_list.html ================================================ {% load allauth %}
      {% slot %} {% endslot %}
    ================================================ FILE: allauth/templates/allauth/elements/table.html ================================================ {% load allauth %} {% slot %} {% endslot %}
    ================================================ FILE: allauth/templates/allauth/elements/tbody.html ================================================ {% load allauth %} {% slot %} {% endslot %} ================================================ FILE: allauth/templates/allauth/elements/td.html ================================================ {% load allauth %} {% slot %} {% endslot %} ================================================ FILE: allauth/templates/allauth/elements/th.html ================================================ {% load allauth %} {% slot %} {% endslot %} ================================================ FILE: allauth/templates/allauth/elements/thead.html ================================================ {% load allauth %} {% slot %} {% endslot %} ================================================ FILE: allauth/templates/allauth/elements/tr.html ================================================ {% load allauth %} {% slot %} {% endslot %} ================================================ FILE: allauth/templates/allauth/layouts/base.html ================================================ {% load i18n %} {% block head_title %} {% endblock head_title %} {% block extra_head %} {% endblock extra_head %} {% block body %} {% if messages %}
    {% trans "Messages:" %}
      {% for message in messages %}
    • {{ message }}
    • {% endfor %}
    {% endif %}
    {% trans "Menu:" %}
    {% block content %} {% endblock content %} {% endblock body %} {% block extra_body %} {% endblock extra_body %} ================================================ FILE: allauth/templates/allauth/layouts/entrance.html ================================================ {% extends "allauth/layouts/base.html" %} {% block content %}{% endblock %} ================================================ FILE: allauth/templates/allauth/layouts/manage.html ================================================ {% extends "allauth/layouts/base.html" %} {% block content %}{% endblock %} ================================================ FILE: allauth/templates/idp/oidc/authorization_form.html ================================================ {% extends "idp/oidc/base.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Authorize" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Authorize" %} {{ client.name }} {% endelement %} {% element p %} {% blocktranslate with client_name=client.name site_name=site.name %}{{ client_name }} wants to access your {{ site_name }} account.{% endblocktranslate %} {% endelement %} {% url 'idp:openid_connect:authorize' as authorize_url %} {% element form method="post" action=authorize_url %} {% slot body %} {% csrf_token %} {{ form }} {% endslot %} {% slot actions %} {% element button_group %} {% element button name="action" value="grant" type="submit" tags="primary,consent" %} {% translate "Authorize" %} {% endelement %} {% element button name="action" value="cancel" type="submit" tags="outline,consent" %} {% translate "Cancel" %} {% endelement %} {% endelement %} {% endslot %} {% endelement %} {% endblock %} ================================================ FILE: allauth/templates/idp/oidc/base.html ================================================ {% extends "allauth/layouts/entrance.html" %} ================================================ FILE: allauth/templates/idp/oidc/device_authorization_code_form.html ================================================ {% extends "idp/oidc/base.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Authorize" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Enter Device Code" %} {% endelement %} {% element p %} {% blocktranslate %}Enter the code displayed on your device.{% endblocktranslate %} {% endelement %} {% element form method="get" action=authorization_url %} {% slot body %} {% element fields form=form unlabeled=True %} {% endelement %} {% endslot %} {% slot actions %} {% element button type="submit" tags="primary" %} {% translate "Continue" %} {% endelement %} {% endslot %} {% endelement %} {% endblock %} ================================================ FILE: allauth/templates/idp/oidc/device_authorization_confirm_form.html ================================================ {% extends "idp/oidc/base.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Authorize" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Confirm Device" %} {% endelement %} {% element p %} {% blocktranslate with client_name=client.name %}Please confirm the code shown on your {{ client_name }} to authorize this device.{% endblocktranslate %} {% endelement %} {% element field type="text" value=user_code disabled=True %} {% slot label %} {% translate "Code" %} {% endslot %} {% endelement %} {% element form method="post" action=authorization_url %} {% slot body %} {{ form }} {% endslot %} {% slot actions %} {% element button_group %} {% element button name="action" value="confirm" type="submit" tags="primary,consent" %} {% translate "Confirm" %} {% endelement %} {% element button name="action" value="deny" type="submit" tags="outline,consent" %} {% translate "Deny" %} {% endelement %} {% endelement %} {% endslot %} {% endelement %} {% endblock %} ================================================ FILE: allauth/templates/idp/oidc/device_authorization_confirmed.html ================================================ {% extends "idp/oidc/base.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Authorize" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Device Authorized" %} {% endelement %} {% element p %} {% blocktranslate with client_name=client.name %}You successfully authorized your {{ client_name }} device.{% endblocktranslate %} {% endelement %} {% endblock %} ================================================ FILE: allauth/templates/idp/oidc/device_authorization_denied.html ================================================ {% extends "idp/oidc/base.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Authorize" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Device Denied" %} {% endelement %} {% element p %} {% blocktranslate with client_name=client.name %}Authorization for your {{ client_name }} device has been denied.{% endblocktranslate %} {% endelement %} {% endblock %} ================================================ FILE: allauth/templates/idp/oidc/error.html ================================================ {% extends "idp/oidc/base.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Error" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Error" %} {% endelement %} {% if error_form %} {{ error_form.errors }} {% else %} {% element p %} {{ error.description }} {% endelement %} {% endif %} {% endblock %} ================================================ FILE: allauth/templates/idp/oidc/logout.html ================================================ {% extends "idp/oidc/base.html" %} {% load allauth i18n %} {% block head_title %} {% trans "Sign Out" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Sign Out" %} {% endelement %} {% element p %} {% trans 'Are you sure you want to sign out?' %} {% endelement %} {% url 'idp:oidc:logout' as action_url %} {% element form method="post" action=action_url no_visible_fields=True %} {% slot body %} {% csrf_token %} {{ form }} {% endslot %} {% slot actions %} {% element button name="action" value="logout" type="submit" %} {% trans 'Sign Out' %} {% endelement %} {% element button name="action" value="stay" tags="secondary" type="submit" %} {% trans 'Stay Signed In' %} {% endelement %} {% endslot %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/mfa/authenticate.html ================================================ {% extends "mfa/base_entrance.html" %} {% load i18n %} {% load allauth %} {% load allauth static %} {% block head_title %} {% trans "Sign In" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Two-Factor Authentication" %} {% endelement %} {% element p %} {% blocktranslate %}Your account is protected by two-factor authentication. Please enter an authenticator code:{% endblocktranslate %} {% endelement %} {% url 'mfa_authenticate' as action_url %} {% element form form=form method="post" action=action_url %} {% slot body %} {% csrf_token %} {% element fields form=form unlabeled=True %} {% endelement %} {% endslot %} {% slot actions %} {% element button type="submit" tags="primary,mfa,login" %} {% trans "Sign In" %} {% endelement %} {% if "webauthn" not in MFA_SUPPORTED_TYPES %} {% element button type="submit" form="logout-from-stage" tags="link,mfa,cancel" %} {% trans "Cancel" %} {% endelement %} {% endif %} {% endslot %} {% endelement %} {% if "webauthn" in MFA_SUPPORTED_TYPES %} {% element hr %} {% endelement %} {% element h2 %} {% translate "Alternative options" %} {% endelement %} {% element button_group vertical=True %} {% element button form="webauthn_form" id="mfa_webauthn_authenticate" type="button" tags="outline,primary" %} {% trans "Use a security key" %} {% endelement %} {% element button type="submit" form="logout-from-stage" tags="outline,primary,mfa,cancel" %} {% trans "Cancel" %} {% endelement %} {% endelement %} {% if "webauthn" in MFA_SUPPORTED_TYPES %} {% element form id="webauthn_form" form=webauthn_form method="post" action=action_url no_visible_fields=True %} {% slot body %} {% csrf_token %} {% element fields form=webauthn_form %} {% endelement %} {% endslot %} {% endelement %} {{ js_data|json_script:"js_data" }} {% include "mfa/webauthn/snippets/scripts.html" %} {% endif %} {% endif %}
    {% csrf_token %}
    {% endblock content %} ================================================ FILE: allauth/templates/mfa/base_entrance.html ================================================ {% extends "allauth/layouts/entrance.html" %} ================================================ FILE: allauth/templates/mfa/base_manage.html ================================================ {% extends "allauth/layouts/manage.html" %} ================================================ FILE: allauth/templates/mfa/email/recovery_codes_generated_message.txt ================================================ {% extends "account/email/base_notification.txt" %} {% load i18n %} {% block notification_message %}{% blocktrans %}A new set of Two-Factor Authentication recovery codes has been generated.{% endblocktrans %}{% endblock notification_message %} ================================================ FILE: allauth/templates/mfa/email/recovery_codes_generated_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}New Recovery Codes Generated{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/mfa/email/totp_activated_message.txt ================================================ {% extends "account/email/base_notification.txt" %} {% load i18n %} {% block notification_message %}{% blocktrans %}Authenticator app activated.{% endblocktrans %}{% endblock notification_message %} ================================================ FILE: allauth/templates/mfa/email/totp_activated_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Authenticator App Activated{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/mfa/email/totp_deactivated_message.txt ================================================ {% extends "account/email/base_notification.txt" %} {% load i18n %} {% block notification_message %}{% blocktrans %}Authenticator app deactivated.{% endblocktrans %}{% endblock notification_message %} ================================================ FILE: allauth/templates/mfa/email/totp_deactivated_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Authenticator App Deactivated{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/mfa/email/webauthn_added_message.txt ================================================ {% extends "account/email/base_notification.txt" %} {% load i18n %} {% block notification_message %}{% blocktrans %}A new security key has been added.{% endblocktrans %}{% endblock notification_message %} ================================================ FILE: allauth/templates/mfa/email/webauthn_added_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Security Key Added{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/mfa/email/webauthn_removed_message.txt ================================================ {% extends "account/email/base_notification.txt" %} {% load i18n %} {% block notification_message %}{% blocktrans %}A security key has been removed.{% endblocktrans %}{% endblock notification_message %} ================================================ FILE: allauth/templates/mfa/email/webauthn_removed_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Security Key Removed{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/mfa/index.html ================================================ {% extends "mfa/base_manage.html" %} {% load allauth %} {% load i18n %} {% block head_title %} {% trans "Two-Factor Authentication" %} {% endblock head_title %} {% block content %} {% element h1 tags="mfa,index" %} {% trans "Two-Factor Authentication" %} {% endelement %} {% if "totp" in MFA_SUPPORTED_TYPES %} {% element panel %} {% slot title %} {% translate "Authenticator App" %} {% endslot %} {% slot body %} {% if authenticators.totp %} {% element p %} {% translate "Authentication using an authenticator app is active." %} {% endelement %} {% else %} {% element p %} {% translate "An authenticator app is not active." %} {% endelement %} {% endif %} {% endslot %} {% slot actions %} {% url 'mfa_deactivate_totp' as deactivate_url %} {% url 'mfa_activate_totp' as activate_url %} {% if authenticators.totp %} {% element button href=deactivate_url tags="danger,delete,panel" %} {% translate "Deactivate" %} {% endelement %} {% else %} {% element button href=activate_url tags="panel" %} {% translate "Activate" %} {% endelement %} {% endif %} {% endslot %} {% endelement %} {% endif %} {% if "webauthn" in MFA_SUPPORTED_TYPES %} {% element panel %} {% slot title %} {% translate "Security Keys" %} {% endslot %} {% slot body %} {% if authenticators.webauthn|length %} {% element p %} {% blocktranslate count count=authenticators.webauthn|length %}You have added {{ count }} security key.{% plural %}You have added {{ count }} security keys.{% endblocktranslate %} {% endelement %} {% else %} {% element p %} {% translate "No security keys have been added." %} {% endelement %} {% endif %} {% endslot %} {% slot actions %} {% if authenticators.webauthn|length %} {% url 'mfa_list_webauthn' as webauthn_list_url %} {% element button href=webauthn_list_url %} {% translate "Manage" %} {% endelement %} {% else %} {% url 'mfa_add_webauthn' as webauthn_add_url %} {% element button href=webauthn_add_url %} {% translate "Add" %} {% endelement %} {% endif %} {% endslot %} {% endelement %} {% endif %} {% if "recovery_codes" in MFA_SUPPORTED_TYPES %} {% with total_count=authenticators.recovery_codes.generate_codes|length unused_count=authenticators.recovery_codes.get_unused_codes|length %} {% element panel %} {% slot title %} {% translate "Recovery Codes" %} {% endslot %} {% slot body %} {% if authenticators.recovery_codes %} {% element p %} {% blocktranslate count unused_count=unused_count %}There is {{ unused_count }} out of {{ total_count }} recovery codes available.{% plural %}There are {{ unused_count }} out of {{ total_count }} recovery codes available.{% endblocktranslate %} {% endelement %} {% else %} {% element p %} {% translate "No recovery codes set up." %} {% endelement %} {% endif %} {% endslot %} {% if is_mfa_enabled %} {% if authenticators.recovery_codes %} {% if unused_count > 0 %} {% slot actions %} {% url 'mfa_view_recovery_codes' as view_url %} {% element button href=view_url tags="panel" %} {% translate "View" %} {% endelement %} {% endslot %} {% slot actions %} {% url 'mfa_download_recovery_codes' as download_url %} {% element button href=download_url tags="secondary,panel" %} {% translate "Download" %} {% endelement %} {% endslot %} {% endif %} {% endif %} {% slot actions %} {% url 'mfa_generate_recovery_codes' as generate_url %} {% element button href=generate_url tags="secondary,panel" %} {% translate "Generate" %} {% endelement %} {% endslot %} {% endif %} {% endelement %} {% endwith %} {% endif %} {% endblock content %} ================================================ FILE: allauth/templates/mfa/messages/recovery_codes_generated.txt ================================================ {% load i18n %} {% blocktrans %}A new set of recovery codes has been generated.{% endblocktrans %} ================================================ FILE: allauth/templates/mfa/messages/totp_activated.txt ================================================ {% load i18n %} {% blocktrans %}Authenticator app activated.{% endblocktrans %} ================================================ FILE: allauth/templates/mfa/messages/totp_deactivated.txt ================================================ {% load i18n %} {% blocktrans %}Authenticator app deactivated.{% endblocktrans %} ================================================ FILE: allauth/templates/mfa/messages/webauthn_added.txt ================================================ {% load i18n %} {% blocktrans %}Security key added.{% endblocktrans %} ================================================ FILE: allauth/templates/mfa/messages/webauthn_removed.txt ================================================ {% load i18n %} {% blocktrans %}Security key removed.{% endblocktrans %} ================================================ FILE: allauth/templates/mfa/reauthenticate.html ================================================ {% extends "account/base_reauthenticate.html" %} {% load i18n %} {% load allauth %} {% block reauthenticate_content %} {% element p %} {% blocktranslate %}Enter an authenticator code:{% endblocktranslate %} {% endelement %} {% url 'mfa_reauthenticate' as action_url %} {% element form form=form method="post" action=action_url %} {% slot body %} {% csrf_token %} {% element fields form=form unlabeled=True %} {% endelement %} {{ redirect_field }} {% endslot %} {% slot actions %} {% element button type="submit" tags="primary,mfa,login" %} {% trans "Confirm" %} {% endelement %} {% endslot %} {% endelement %} {% endblock %} ================================================ FILE: allauth/templates/mfa/recovery_codes/base.html ================================================ {% extends "mfa/base_manage.html" %} {% load i18n %} {% block head_title %} {% trans "Recovery Codes" %} {% endblock head_title %} ================================================ FILE: allauth/templates/mfa/recovery_codes/download.txt ================================================ {% for code in unused_codes %}{{ code }} {% endfor %} ================================================ FILE: allauth/templates/mfa/recovery_codes/generate.html ================================================ {% extends "mfa/recovery_codes/base.html" %} {% load i18n %} {% load allauth %} {% block content %} {% element h1 %} {% translate "Recovery Codes" %} {% endelement %} {% element p %} {% blocktranslate %}You are about to generate a new set of recovery codes for your account.{% endblocktranslate %} {% if unused_code_count %} {% blocktranslate %}This action will invalidate your existing codes.{% endblocktranslate %} {% endif %} {% blocktranslate %}Are you sure?{% endblocktranslate %} {% endelement %} {% url 'mfa_generate_recovery_codes' as action_url %} {% element form method="post" action=action_url no_visible_fields=True %} {% slot body %} {% csrf_token %} {{ form.as_p }} {% endslot %} {% slot actions %} {% setvar tags %} {% if unused_code_count %} danger {% else %} {% endif %} {% endsetvar %} {% element button type="submit" tags=tags %} {% trans "Generate" %} {% endelement %} {% endslot %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/mfa/recovery_codes/index.html ================================================ {% extends "mfa/recovery_codes/base.html" %} {% load i18n %} {% load allauth %} {% block content %} {% element h1 %} {% translate "Recovery Codes" %} {% endelement %} {% element p %} {% blocktranslate count unused_count=unused_codes|length %}There is {{ unused_count }} out of {{ total_count }} recovery codes available.{% plural %}There are {{ unused_count }} out of {{ total_count }} recovery codes available.{% endblocktranslate %} {% endelement %} {% element field id="recovery_codes" type="textarea" disabled=True rows=unused_codes|length readonly=True %} {% slot label %} {% translate "Unused codes" %} {% endslot %} {% comment %} djlint:off {% endcomment %} {% slot value %}{% for code in unused_codes %}{% if forloop.counter0 %} {% endif %}{{ code }}{% endfor %}{% endslot %} {% comment %} djlint:on {% endcomment %} {% endelement %} {% if unused_codes %} {% url 'mfa_download_recovery_codes' as download_url %} {% element button href=download_url %} {% translate "Download codes" %} {% endelement %} {% endif %} {% url 'mfa_generate_recovery_codes' as generate_url %} {% element button href=generate_url %} {% translate "Generate new codes" %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/mfa/totp/activate_form.html ================================================ {% extends "mfa/totp/base.html" %} {% load allauth i18n %} {% block head_title %} {% translate "Activate Authenticator App" %} {% endblock head_title %} {% block content %} {% element h1 %} {% translate "Activate Authenticator App" %} {% endelement %} {% element p %} {% blocktranslate %}To protect your account with two-factor authentication, scan the QR code below with your authenticator app. Then, input the verification code generated by the app below.{% endblocktranslate %} {% endelement %} {% url 'mfa_activate_totp' as action_url %} {% element form form=form method="post" action=action_url %} {% slot body %} {% element img src=totp_svg_data_uri alt=form.secret tags="mfa,totp,qr" %} {% endelement %} {% csrf_token %} {% element field id="authenticator_secret" type="text" value=form.secret disabled=True %} {% slot label %} {% translate "Authenticator secret" %} {% endslot %} {% slot help_text %} {% translate "You can store this secret and use it to reinstall your authenticator app at a later time." %} {% endslot %} {% endelement %} {% element fields form=form %} {% endelement %} {% endslot %} {% slot actions %} {% element button type="submit" %} {% trans "Activate" %} {% endelement %} {% endslot %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/mfa/totp/base.html ================================================ {% extends "mfa/base_manage.html" %} {% load i18n %} {% block head_title %} {% trans "Authenticator App" %} {% endblock head_title %} ================================================ FILE: allauth/templates/mfa/totp/deactivate_form.html ================================================ {% extends "mfa/totp/base.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Deactivate Authenticator App" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Deactivate Authenticator App" %} {% endelement %} {% element p %} {% blocktranslate %}You are about to deactivate authenticator app based authentication. Are you sure?{% endblocktranslate %} {% endelement %} {% url 'mfa_deactivate_totp' as action_url %} {% element form form=form method="post" action=action_url no_visible_fields=True %} {% slot body %} {% csrf_token %} {% element fields form=form %} {{ form.as_p }} {% endelement %} {% endslot %} {% slot actions %} {% element button tags="danger,delete" type="submit" %} {% trans "Deactivate" %} {% endelement %} {% endslot %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/mfa/trust.html ================================================ {% extends "mfa/base_entrance.html" %} {% load i18n allauth static %} {% block head_title %} {% trans "Trust this Browser?" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Trust this Browser?" %} {% endelement %} {% element p %} {% blocktranslate %}If you choose to trust this browser, you will not be asked for a verification code the next time you sign in.{% endblocktranslate %} {% endelement %} {% url 'mfa_trust' as action_url %} {% element form form=form method="post" action=action_url %} {% slot body %} {% csrf_token %} {% element fields form=form unlabeled=True %} {% endelement %} {% endslot %} {% slot actions %} {% element button_group %} {% element button name="action" value="trust" type="submit" tags="primary,mfa,trust" %} {% blocktranslate with period=trust_until|timeuntil:trust_from %}Trust for {{ period }}{% endblocktranslate %} {% endelement %} {% element button name="action" value="skip" type="submit" tags="outline,mfa,trust" %} {% trans "Don't Trust" %} {% endelement %} {% element button type="submit" form="logout-from-stage" tags="outline,mfa,trust,cancel" %} {% trans "Cancel" %} {% endelement %} {% endelement %} {% endslot %} {% endelement %}
    {% csrf_token %}
    {% endblock content %} ================================================ FILE: allauth/templates/mfa/webauthn/add_form.html ================================================ {% extends "mfa/webauthn/base.html" %} {% load i18n %} {% load static %} {% load allauth %} {% block content %} {% element h1 %} {% trans "Add Security Key" %} {% endelement %} {% url 'mfa_add_webauthn' as action_url %} {% element form form=form method="post" action=action_url %} {% slot body %} {% csrf_token %} {% element fields form=form %} {% endelement %} {% endslot %} {% slot actions %} {% element button id="mfa_webauthn_add" type="button" %} {% trans "Add" %} {% endelement %} {% endslot %} {% endelement %} {% include "mfa/webauthn/snippets/scripts.html" %} {{ js_data|json_script:"js_data" }} {% endblock %} ================================================ FILE: allauth/templates/mfa/webauthn/authenticator_confirm_delete.html ================================================ {% extends "mfa/webauthn/base.html" %} {% load i18n %} {% load allauth %} {% block content %} {% element h1 %} {% trans "Remove Security Key" %} {% endelement %} {% element p %} {% blocktranslate %}Are you sure you want to remove this security key?{% endblocktranslate %} {% endelement %} {% url 'mfa_remove_webauthn' pk=authenticator.pk as action_url %} {% element form method="post" action=action_url no_visible_fields=True %} {% slot actions %} {% csrf_token %} {% element button tags="danger" type="submit" %} {% translate "Remove" %} {% endelement %} {% endslot %} {% endelement %} {% endblock %} ================================================ FILE: allauth/templates/mfa/webauthn/authenticator_list.html ================================================ {% extends "mfa/webauthn/base.html" %} {% load i18n %} {% load static %} {% load allauth %} {% load humanize %} {% block content %} {% element h1 %} {% trans "Security Keys" %} {% endelement %} {% if authenticators|length == 0 %} {% element p %} {% blocktranslate %}No security keys have been added.{% endblocktranslate %} {% endelement %} {% else %} {% element table %} {% element thead %} {% element th %} {% translate "Key" %} {% endelement %} {% element th %} {% translate "Usage" %} {% endelement %} {% element th %} {% endelement %} {% endelement %} {% element tbody %} {% for authenticator in authenticators %} {% element tr %} {% element td %} {{ authenticator }} {% if authenticator.wrap.is_passwordless is True %} {% element badge tags="mfa,key,primary" %} {% translate "Passkey" %} {% endelement %} {% elif authenticator.wrap.is_passwordless is False %} {% element badge tags="mfa,key,secondary" %} {% translate "Security key" %} {% endelement %} {% else %} {% element badge title=_("This key does not indicate whether it is a passkey.") tags="mfa,key,warning" %} {% translate "Unspecified" %} {% endelement %} {% endif %} {% endelement %} {% element td %} {% blocktranslate with created_at=authenticator.created_at|date:"SHORT_DATE_FORMAT" %}Added on {{ created_at }}{% endblocktranslate %}. {% if authenticator.last_used_at %} {% blocktranslate with last_used=authenticator.last_used_at|naturaltime %}Last used {{ last_used }}{% endblocktranslate %} {% else %} Not used. {% endif %} {% endelement %} {% element td align="right" %} {% url 'mfa_edit_webauthn' pk=authenticator.pk as edit_url %} {% element button tags="mfa,authenticator,edit,tool" href=edit_url %} {% translate "Edit" %} {% endelement %} {% url 'mfa_remove_webauthn' pk=authenticator.pk as remove_url %} {% element button tags="mfa,authenticator,danger,delete,tool" href=remove_url %} {% translate "Remove" %} {% endelement %} {% endelement %} {% endelement %} {% endfor %} {% endelement %} {% endelement %} {% endif %} {% url 'mfa_add_webauthn' as add_url %} {% element button href=add_url %} {% translate "Add" %} {% endelement %} {% endblock %} ================================================ FILE: allauth/templates/mfa/webauthn/base.html ================================================ {% extends "mfa/base_manage.html" %} {% load i18n %} {% block head_title %} {% trans "Security Keys" %} {% endblock head_title %} ================================================ FILE: allauth/templates/mfa/webauthn/edit_form.html ================================================ {% extends "mfa/webauthn/base.html" %} {% load i18n %} {% load static %} {% load allauth %} {% block content %} {% element h1 %} {% trans "Edit Security Key" %} {% endelement %} {% url 'mfa_edit_webauthn' as action_url %} {% element form form=form method="post" action=action_url %} {% slot body %} {% csrf_token %} {% element fields form=form %} {% endelement %} {% endslot %} {% slot actions %} {% element button id="mfa_webauthn_edit" type="submit" %} {% trans "Save" %} {% endelement %} {% endslot %} {% endelement %} {% endblock %} ================================================ FILE: allauth/templates/mfa/webauthn/reauthenticate.html ================================================ {% extends "account/base_reauthenticate.html" %} {% load i18n %} {% load allauth %} {% block reauthenticate_content %} {% url 'mfa_reauthenticate_webauthn' as action_url %} {% element form form=form method="post" action=action_url %} {% slot body %} {% csrf_token %} {% element fields form=form unlabeled=True %} {% endelement %} {{ redirect_field }} {% endslot %} {% slot actions %} {% element button id="mfa_webauthn_reauthenticate" type="submit" tags="primary,mfa,login" %} {% trans "Use a security key" %} {% endelement %} {% endslot %} {% endelement %} {{ js_data|json_script:"js_data" }} {% include "mfa/webauthn/snippets/scripts.html" %} {% endblock %} ================================================ FILE: allauth/templates/mfa/webauthn/signup_form.html ================================================ {% extends "account/base_entrance.html" %} {% load i18n %} {% load static %} {% load allauth %} {% block content %} {% element h1 %} {% trans "Create Passkey" %} {% endelement %} {% element p %} {% blocktranslate %}You are about to create a passkey for your account. As you can add additional keys later on, you can use a descriptive name to tell the keys apart.{% endblocktranslate %} {% endelement %} {% url 'mfa_signup_webauthn' as action_url %} {% element form form=form method="post" action=action_url %} {% slot body %} {% csrf_token %} {% element fields form=form %} {% endelement %} {% endslot %} {% slot actions %} {% element button id="mfa_webauthn_signup" type="button" %} {% trans "Create" %} {% endelement %} {% endslot %} {% endelement %} {% element button type="submit" form="logout-from-stage" tags="link,cancel" %} {% translate "Cancel" %} {% endelement %}
    {% csrf_token %}
    {% include "mfa/webauthn/snippets/scripts.html" %} {{ js_data|json_script:"js_data" }} {% endblock %} ================================================ FILE: allauth/templates/mfa/webauthn/snippets/login_script.html ================================================ {% include "mfa/webauthn/snippets/scripts.html" %}
    {% csrf_token %} {{ redirect_field }}
    ================================================ FILE: allauth/templates/mfa/webauthn/snippets/scripts.html ================================================ {% load i18n static %} ================================================ FILE: allauth/templates/openid/base.html ================================================ {% extends "socialaccount/base_entrance.html" %} ================================================ FILE: allauth/templates/openid/login.html ================================================ {% extends "openid/base.html" %} {% load i18n %} {% load allauth %} {% block head_title %} OpenID {% translate "Sign In" %} {% endblock head_title %} {% block content %} {% element h1 %} OpenID {% trans 'Sign In' %} {% endelement %} {% url 'openid_login' as action_url %} {% element form method="post" action=action_url %} {% slot body %} {% csrf_token %} {% element fields form=form unlabeled=True %} {% endelement %} {% endslot %} {% slot actions %} {% element button type="submit" %} {% translate "Sign In" %} {% endelement %} {% endslot %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/socialaccount/authentication_error.html ================================================ {% extends "socialaccount/base_entrance.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Third-Party Login Failure" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Third-Party Login Failure" %} {% endelement %} {% element p %} {% trans "An error occurred while attempting to login via your third-party account." %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/socialaccount/base_entrance.html ================================================ {% extends "allauth/layouts/entrance.html" %} ================================================ FILE: allauth/templates/socialaccount/base_manage.html ================================================ {% extends "allauth/layouts/manage.html" %} ================================================ FILE: allauth/templates/socialaccount/connections.html ================================================ {% extends "socialaccount/base_manage.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Account Connections" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Account Connections" %} {% endelement %} {% if form.accounts %} {% element p %} {% blocktrans %}You can sign in to your account using any of the following third-party accounts:{% endblocktrans %} {% endelement %} {% url 'socialaccount_connections' as action_url %} {% element form form=form method="post" action=action_url %} {% slot body %} {% csrf_token %} {% for acc in form.fields.account.choices %} {% with account=acc.0.instance.get_provider_account %} {% setvar radio_id %} id_account_{{ account.account.pk }} {% endsetvar %} {% setvar tags %} socialaccount,{{ account.account.provider }} {% endsetvar %} {% element field id=radio_id type="radio" name="account" value=account.account.pk %} {% slot label %} {{ account }} {% element badge tags=tags %} {{ account.get_brand.name }} {% endelement %} {% endslot %} {% endelement %} {% endwith %} {% endfor %} {% endslot %} {% slot actions %} {% element button tags="delete,danger" type="submit" %} {% trans 'Remove' %} {% endelement %} {% endslot %} {% endelement %} {% else %} {% element p %} {% trans 'You currently have no third-party accounts connected to this account.' %} {% endelement %} {% endif %} {% element h2 %} {% trans 'Add a Third-Party Account' %} {% endelement %} {% include "socialaccount/snippets/provider_list.html" with process="connect" %} {% include "socialaccount/snippets/login_extra.html" %} {% endblock content %} ================================================ FILE: allauth/templates/socialaccount/email/account_connected_message.txt ================================================ {% extends "account/email/base_notification.txt" %} {% load i18n %} {% block notification_message %}{% blocktrans %}A third-party account from {{ provider }} has been connected to your account.{% endblocktrans %}{% endblock notification_message %} ================================================ FILE: allauth/templates/socialaccount/email/account_connected_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Third-Party Account Connected{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/socialaccount/email/account_disconnected_message.txt ================================================ {% extends "account/email/base_notification.txt" %} {% load i18n %} {% block notification_message %}{% blocktrans %}A third-party account from {{ provider }} has been disconnected from your account.{% endblocktrans %}{% endblock notification_message %} ================================================ FILE: allauth/templates/socialaccount/email/account_disconnected_subject.txt ================================================ {% load i18n %} {% autoescape off %} {% blocktrans %}Third-Party Account Disconnected{% endblocktrans %} {% endautoescape %} ================================================ FILE: allauth/templates/socialaccount/login.html ================================================ {% extends "socialaccount/base_entrance.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Sign In" %} {% endblock head_title %} {% block content %} {% if process == "connect" %} {% element h1 %} {% blocktrans with provider.name as provider %}Connect {{ provider }}{% endblocktrans %} {% endelement %} {% element p %} {% blocktrans with provider.name as provider %}You are about to connect a new third-party account from {{ provider }}.{% endblocktrans %} {% endelement %} {% else %} {% element h1 %} {% blocktrans with provider.name as provider %}Sign In Via {{ provider }}{% endblocktrans %} {% endelement %} {% element p %} {% blocktrans with provider.name as provider %}You are about to sign in using a third-party account from {{ provider }}.{% endblocktrans %} {% endelement %} {% endif %} {% element form method="post" no_visible_fields=True %} {% slot actions %} {% csrf_token %} {% element button type="submit" %} {% trans "Continue" %} {% endelement %} {% endslot %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/socialaccount/login_cancelled.html ================================================ {% extends "socialaccount/base_entrance.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Login Cancelled" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Login Cancelled" %} {% endelement %} {% url 'account_login' as login_url %} {% element p %} {% blocktrans %}You decided to cancel logging in to our site using one of your existing accounts. If this was a mistake, please proceed to sign in.{% endblocktrans %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/socialaccount/login_redirect.html ================================================ {% load i18n allauth %} {% translate "Sign In" %} | {{ provider }} {% element p %} {% translate "Continue" %} {% endelement %} ================================================ FILE: allauth/templates/socialaccount/messages/account_connected.txt ================================================ {% load i18n %} {% blocktrans %}The third-party account has been connected.{% endblocktrans %} ================================================ FILE: allauth/templates/socialaccount/messages/account_connected_other.txt ================================================ {% load i18n %} {% blocktrans %}The third-party account is already connected to a different account.{% endblocktrans %} ================================================ FILE: allauth/templates/socialaccount/messages/account_connected_updated.txt ================================================ {% extends "socialaccount/messages/account_connected.txt" %} ================================================ FILE: allauth/templates/socialaccount/messages/account_disconnected.txt ================================================ {% load i18n %} {% blocktrans %}The third-party account has been disconnected.{% endblocktrans %} ================================================ FILE: allauth/templates/socialaccount/signup.html ================================================ {% extends "socialaccount/base_entrance.html" %} {% load i18n %} {% load allauth %} {% block head_title %} {% trans "Signup" %} {% endblock head_title %} {% block content %} {% element h1 %} {% trans "Sign Up" %} {% endelement %} {% element p %} {% blocktrans with provider_name=account.get_provider.name site_name=site.name %}You are about to use your {{provider_name}} account to login to {{site_name}}. As a final step, please complete the following form:{% endblocktrans %} {% endelement %} {% url 'socialaccount_signup' as action_url %} {% element form form=form method="post" action=action_url %} {% slot body %} {% csrf_token %} {% element fields form=form unlabeled=True %} {% endelement %} {{ redirect_field }} {% endslot %} {% slot actions %} {% element button type="submit" %} {% trans "Sign Up" %} {% endelement %} {% endslot %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templates/socialaccount/snippets/login.html ================================================ {% load i18n %} {% load allauth %} {% load socialaccount %} {% get_providers as socialaccount_providers %} {% if socialaccount_providers %} {% if not SOCIALACCOUNT_ONLY %} {% element hr %} {% endelement %} {% element h2 %} {% translate "Or use a third-party" %} {% endelement %} {% endif %} {% include "socialaccount/snippets/provider_list.html" with process="login" %} {% include "socialaccount/snippets/login_extra.html" %} {% endif %} ================================================ FILE: allauth/templates/socialaccount/snippets/login_extra.html ================================================ {% load socialaccount %} {% providers_media_js %} ================================================ FILE: allauth/templates/socialaccount/snippets/provider_list.html ================================================ {% load allauth socialaccount %} {% get_providers as socialaccount_providers %} {% if socialaccount_providers %} {% element provider_list %} {% for provider in socialaccount_providers %} {% if provider.id == "openid" %} {% for brand in provider.get_brands %} {% provider_login_url provider openid=brand.openid_url process=process as href %} {% element provider name=brand.name provider_id=provider.id href=href %} {% endelement %} {% endfor %} {% endif %} {% provider_login_url provider process=process scope=scope auth_params=auth_params as href %} {% element provider name=provider.name provider_id=provider.id href=href %} {% endelement %} {% endfor %} {% endelement %} {% endif %} ================================================ FILE: allauth/templates/usersessions/base_manage.html ================================================ {% extends "allauth/layouts/manage.html" %} ================================================ FILE: allauth/templates/usersessions/messages/sessions_logged_out.txt ================================================ {% load i18n %} {% blocktranslate %}Signed out of all other sessions.{% endblocktranslate %} ================================================ FILE: allauth/templates/usersessions/usersession_list.html ================================================ {% extends "usersessions/base_manage.html" %} {% load allauth %} {% load i18n %} {% load humanize %} {% block head_title %} {% trans "Sessions" %} {% endblock head_title %} {% block content %} {% element h1 tags="usersessions,list" %} {% trans "Sessions" %} {% endelement %} {% if session_count > 1 %} {% url 'usersessions_list' as action_url %} {% else %} {% url 'account_logout' as action_url %} {% endif %} {% element form action=action_url method="post" tags="sessions" no_visible_fields=True %} {% slot body %} {% csrf_token %} {% element table tags="sessions" %} {% element thead %} {% element tr %} {% element th %} {% translate "Started At" %} {% endelement %} {% element th %} {% translate "IP Address" %} {% endelement %} {% element th %} {% translate "Browser" %} {% endelement %} {% if show_last_seen_at %} {% element th %} {% translate "Last seen at" %} {% endelement %} {% endif %} {% endelement %} {% endelement %} {% element tbody %} {% for session in sessions %} {% element tr %} {% element td %} {{ session.created_at|naturaltime }} {% endelement %} {% element td %} {{ session.ip }} {% endelement %} {% element td %} {{ session.user_agent }} {% endelement %} {% if show_last_seen_at %} {% element td %} {{ session.last_seen_at|naturaltime }} {% endelement %} {% endif %} {% element td %} {% if session.is_current %} {% element badge tags="session,current" %} {% translate "Current" %} {% endelement %} {% else %} {% endif %} {% endelement %} {% endelement %} {% endfor %} {% endelement %} {% endelement %} {% endslot %} {% slot actions %} {% if session_count > 1 %} {% element button type="submit" %} {% translate "Sign Out Other Sessions" %} {% endelement %} {% else %} {% element button type="submit" %} {% translate "Sign Out" %} {% endelement %} {% endif %} {% endslot %} {% endelement %} {% endblock content %} ================================================ FILE: allauth/templatetags/__init__.py ================================================ ================================================ FILE: allauth/templatetags/allauth.py ================================================ from django import template from django.template.base import FilterExpression, kwarg_re from django.template.loader import render_to_string from django.template.loader_tags import ExtendsNode from django.utils.safestring import mark_safe SLOTS_CONTEXT_KEY = "slots_context" LAYOUT_CONTEXT_KEY = "layout_context" def parse_tag(token, parser): bits = token.split_contents() tag_name = bits.pop(0) args = [] kwargs = {} for bit in bits: # Is this a kwarg or an arg? match = kwarg_re.match(bit) kwarg_format = match and match.group(1) if kwarg_format: key, value = match.groups() kwargs[key] = FilterExpression(value, parser) else: args.append(FilterExpression(bit, parser)) return (tag_name, args, kwargs) register = template.Library() @register.tag(name="slot") def do_slot(parser, token): nodelist = parser.parse(("endslot",)) bits = token.split_contents() bits.pop(0) slot_name = bits.pop(0) if bits else "default" parser.delete_first_token() return SlotNode(slot_name, nodelist) class SlotNode(template.Node): def __init__(self, name, nodelist): self.name = name self.nodelist = nodelist def render(self, context): slots = context.render_context.get(SLOTS_CONTEXT_KEY) with context.push(): if slots is None: if self.name in context["slots"]: return "".join(context["slots"][self.name]) return self.nodelist.render(context) else: result = self.nodelist.render(context) slot_list = slots.setdefault(self.name, []) slot_list.append(result) return "" @register.tag(name="element") def do_element(parser, token): nodelist = parser.parse(("endelement",)) tag_name, args, kwargs = parse_tag(token, parser) usage = f'{{% {tag_name} "element" argument=value %}} ... {{% end{tag_name} %}}' if len(args) > 1: raise template.TemplateSyntaxError(f"Usage: {usage}") parser.delete_first_token() return ElementNode(nodelist, args[0], kwargs) class ElementNode(template.Node): def __init__(self, nodelist, element, kwargs): self.element = element self.kwargs = kwargs self.nodelist = nodelist def render(self, context): from allauth.account.app_settings import TEMPLATE_EXTENSION slots = {} extends_context = context.render_context.get(ExtendsNode.context_key) layout = None if extends_context: # Extract layout from the {% extends %} tags for ec in extends_context: prefix = "allauth/layouts/" if ec.template_name.startswith(prefix): layout = ec.template_name[len(prefix) :].replace(".html", "") break if not layout: # In case we're in a {% element %} element, the extends context is # not there. layout = context.render_context.get(LAYOUT_CONTEXT_KEY) if not layout: # Or, similarly, for {% include %} we also lose the extends context. layout = context.get("page_layout") template_names = [] if layout: template_names.append(f"allauth/elements/{self.element}__{layout}.html") template_names.append(f"allauth/elements/{self.element}.html") with context.render_context.push( **{SLOTS_CONTEXT_KEY: slots, LAYOUT_CONTEXT_KEY: layout} ): slots["default"] = [self.nodelist.render(context)] attrs = {} for k, v in self.kwargs.items(): attrs[k] = v.resolve(context) tags = attrs.get("tags") if tags: attrs["tags"] = [tag.strip() for tag in tags.split(",")] with context.push( slots=slots, attrs=attrs, origin=self.origin.template_name.replace(f".{TEMPLATE_EXTENSION}", ""), ) as element_context: return render_to_string( template_names, element_context.context.flatten() ) @register.tag(name="setvar") def do_setvar(parser, token): nodelist = parser.parse(("endsetvar",)) bits = token.split_contents() if len(bits) != 2: tag_name = bits[0] usage = f'{{% {tag_name} "setvar" var %}} ... {{% end{tag_name} %}}' raise template.TemplateSyntaxError(f"Usage: {usage}") parser.delete_first_token() return SetVarNode(nodelist, bits[1]) class SetVarNode(template.Node): def __init__(self, nodelist, var): self.nodelist = nodelist self.var = var def render(self, context): context[self.var] = mark_safe(self.nodelist.render(context).strip()) # nosec return "" ================================================ FILE: allauth/urls.py ================================================ from importlib import import_module from django.urls import URLPattern, URLResolver, include, path from django.views.generic.base import RedirectView from allauth.socialaccount import providers from . import app_settings def build_provider_urlpatterns() -> list[URLPattern | URLResolver]: # Provider urlpatterns, as separate attribute (for reusability). provider_urlpatterns: list[URLPattern | URLResolver] = [] provider_classes = providers.registry.get_class_list() # We need to move the OpenID Connect provider to the end. The reason is that # matches URLs that the builtin providers also match. # # NOTE: Only needed if OPENID_CONNECT_URL_PREFIX is blank. provider_classes = [ cls for cls in provider_classes if cls.id != "openid_connect" ] + [cls for cls in provider_classes if cls.id == "openid_connect"] for provider_class in provider_classes: prov_mod = import_module(f"{provider_class.get_package()}.urls") prov_urlpatterns = getattr(prov_mod, "urlpatterns", None) if prov_urlpatterns: provider_urlpatterns += prov_urlpatterns return provider_urlpatterns urlpatterns: list[URLPattern | URLResolver] = [] if not app_settings.HEADLESS_ONLY: urlpatterns += [path("", include("allauth.account.urls"))] if app_settings.MFA_ENABLED: urlpatterns += [path("2fa/", include("allauth.mfa.urls"))] if app_settings.SOCIALACCOUNT_ENABLED and not app_settings.HEADLESS_ONLY: urlpatterns += [path("3rdparty/", include("allauth.socialaccount.urls"))] # DEPRECATED! -- deal with legacy URLs urlpatterns += [ path( "social/login/cancelled/", RedirectView.as_view( pattern_name="socialaccount_login_cancelled", permanent=True ), ), path( "social/login/error/", RedirectView.as_view( pattern_name="socialaccount_login_error", permanent=True ), ), path( "social/signup/", RedirectView.as_view(pattern_name="socialaccount_signup", permanent=True), ), path( "social/connections/", RedirectView.as_view( pattern_name="socialaccount_connections", permanent=True ), ), ] # (end DEPRECATED) if app_settings.SOCIALACCOUNT_ENABLED: urlpatterns += build_provider_urlpatterns() if app_settings.USERSESSIONS_ENABLED and not app_settings.HEADLESS_ONLY: urlpatterns += [path("sessions/", include("allauth.usersessions.urls"))] ================================================ FILE: allauth/usersessions/__init__.py ================================================ ================================================ FILE: allauth/usersessions/adapter.py ================================================ from allauth.core.internal.adapter import BaseAdapter from allauth.usersessions import app_settings from allauth.utils import import_attribute class DefaultUserSessionsAdapter(BaseAdapter): """The adapter class allows you to override various functionality of the ``allauth.usersessions`` app. To do so, point ``settings.USERSESSIONS_ADAPTER`` to your own class that derives from ``DefaultUserSessionsAdapter`` and override the behavior by altering the implementation of the methods according to your own needs. """ def end_sessions(self, sessions) -> None: for session in sessions: session.end() def get_adapter() -> DefaultUserSessionsAdapter: return import_attribute(app_settings.ADAPTER)() ================================================ FILE: allauth/usersessions/admin.py ================================================ from django.contrib import admin from allauth.usersessions.models import UserSession @admin.register(UserSession) class UserSessionAdmin(admin.ModelAdmin): raw_id_fields = ("user",) list_display = ("user", "created_at", "last_seen_at", "ip", "user_agent") search_fields = [ "user__pk", "user__username", "ip", ] ================================================ FILE: allauth/usersessions/app_settings.py ================================================ class AppSettings: def __init__(self, prefix: str) -> None: self.prefix = prefix def _setting(self, name: str, dflt): from allauth.utils import get_setting return get_setting(self.prefix + name, dflt) @property def ADAPTER(self) -> str: return self._setting( "ADAPTER", "allauth.usersessions.adapter.DefaultUserSessionsAdapter" ) @property def TRACK_ACTIVITY(self) -> bool: """Whether or not sessions are to be actively tracked. When tracking is enabled, the last seen IP address and last seen timestamp will be kept track of. """ return self._setting("TRACK_ACTIVITY", False) _app_settings = AppSettings("USERSESSIONS_") def __getattr__(name): # See https://peps.python.org/pep-0562/ return getattr(_app_settings, name) ================================================ FILE: allauth/usersessions/apps.py ================================================ from django.apps import AppConfig from django.utils.translation import gettext_lazy as _ from allauth import app_settings class UserSessionsConfig(AppConfig): name = "allauth.usersessions" verbose_name = _("User Sessions") default_auto_field = ( app_settings.DEFAULT_AUTO_FIELD or "django.db.models.BigAutoField" ) def ready(self): from allauth.account.signals import ( password_changed, password_set, user_logged_in, ) from allauth.usersessions import signals user_logged_in.connect(receiver=signals.on_user_logged_in) for sig in [password_set, password_changed]: sig.connect(receiver=signals.on_password_changed) ================================================ FILE: allauth/usersessions/forms.py ================================================ from django import forms from allauth.usersessions.internal import flows class ManageUserSessionsForm(forms.Form): def __init__(self, *args, **kwargs) -> None: self.request = kwargs.pop("request") super().__init__(*args, **kwargs) def save(self, request) -> None: flows.sessions.end_other_sessions(request, request.user) ================================================ FILE: allauth/usersessions/internal/__init__.py ================================================ ================================================ FILE: allauth/usersessions/internal/flows/__init__.py ================================================ from allauth.usersessions.internal.flows import sessions __all__ = ["sessions"] ================================================ FILE: allauth/usersessions/internal/flows/sessions.py ================================================ from allauth.account.internal import flows from allauth.usersessions.adapter import get_adapter from allauth.usersessions.models import UserSession def end_other_sessions(request, user): sessions_to_end = [] for session in UserSession.objects.filter(user=user): if session.is_current(): continue sessions_to_end.append(session) end_sessions(request, sessions_to_end) def end_sessions(request, sessions): has_current = any([session.is_current() for session in sessions]) get_adapter().end_sessions(sessions) if has_current: flows.logout.logout(request) ================================================ FILE: allauth/usersessions/middleware.py ================================================ from allauth.usersessions import app_settings from allauth.usersessions.models import UserSession class UserSessionsMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): if ( app_settings.TRACK_ACTIVITY and hasattr(request, "session") and request.session.session_key and hasattr(request, "user") and request.user.is_authenticated ): UserSession.objects.create_from_request(request) response = self.get_response(request) return response ================================================ FILE: allauth/usersessions/migrations/0001_initial.py ================================================ # Generated by Django 4.2.6 on 2023-12-05 11:44 import django.db.models.deletion import django.utils.timezone from django.conf import settings from django.db import migrations, models class Migration(migrations.Migration): initial = True dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name="UserSession", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ("created_at", models.DateTimeField(default=django.utils.timezone.now)), ("ip", models.GenericIPAddressField()), ( "last_seen_at", models.DateTimeField(default=django.utils.timezone.now), ), ( "session_key", models.CharField( editable=False, max_length=40, unique=True, verbose_name="session key", ), ), ("user_agent", models.CharField(max_length=200)), ("data", models.JSONField(default=dict)), ( "user", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, ), ), ], ), ] ================================================ FILE: allauth/usersessions/migrations/__init__.py ================================================ ================================================ FILE: allauth/usersessions/models.py ================================================ from importlib import import_module from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.db import models, transaction from django.http import HttpRequest from django.utils import timezone from django.utils.translation import gettext_lazy as _ from allauth import app_settings as allauth_settings from allauth.account.adapter import get_adapter from allauth.core import context from allauth.core.internal.httpkit import HTTP_USER_AGENT_MAX_LENGTH from allauth.core.internal.sessionkit import get_session_user if not allauth_settings.USERSESSIONS_ENABLED: raise ImproperlyConfigured( "allauth.usersessions not installed, yet its models are imported." ) class UserSessionManager(models.Manager): def purge_and_list(self, user) -> list["UserSession"]: ret = [] sessions = UserSession.objects.filter(user=user) for session in sessions.iterator(): if not session.purge(): ret.append(session) return ret def create_from_request(self, request: HttpRequest): if not request.user.is_authenticated: raise ValueError() if not request.session.session_key: request.session.save() ua = request.META.get("HTTP_USER_AGENT", "")[ 0 : UserSession._meta.get_field("user_agent").max_length ] defaults = dict( user=request.user, ip=get_adapter().get_client_ip(request), user_agent=ua, ) from_session = None with transaction.atomic(): from allauth.usersessions.signals import session_client_changed session, created = UserSession.objects.get_or_create( session_key=request.session.session_key, defaults=defaults ) if not created: from_session = UserSession( session_key=session.session_key, user=session.user, ip=session.ip, user_agent=session.user_agent, data=session.data, created_at=session.created_at, last_seen_at=session.last_seen_at, ) # Update session session.user = defaults["user"] session.ip = defaults["ip"] session.user_agent = defaults["user_agent"] session.last_seen_at = timezone.now() session.save() if from_session and ( from_session.ip != session.ip or from_session.user_agent != session.user_agent ): session_client_changed.send( sender=UserSession, request=request, from_session=from_session, to_session=session, ) class UserSession(models.Model): objects = UserSessionManager() user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) created_at = models.DateTimeField(default=timezone.now) ip = models.GenericIPAddressField() last_seen_at = models.DateTimeField(default=timezone.now) session_key = models.CharField( _("session key"), max_length=40, unique=True, editable=False ) user_agent = models.CharField(max_length=HTTP_USER_AGENT_MAX_LENGTH) data = models.JSONField(default=dict) def __str__(self) -> str: return f"{self.ip} ({self.user_agent})" def _session_store(self, *args): engine = import_module(settings.SESSION_ENGINE) return engine.SessionStore(*args) def exists(self) -> bool: return self._session_store().exists(self.session_key) def purge(self) -> bool: purge = not self.exists() if not purge: # Even if the session still exists, it might be the case that the # user session hash is out of sync. So, let's see if # `django.contrib.auth` can find a user... user = get_session_user(self._session_store(self.session_key)) purge = not user if purge: self.delete() return True return False def is_current(self) -> bool: return self.session_key == context.request.session.session_key def end(self) -> None: engine = import_module(settings.SESSION_ENGINE) store = engine.SessionStore() store.delete(self.session_key) self.delete() ================================================ FILE: allauth/usersessions/signals.py ================================================ from django.dispatch import Signal from allauth.account import app_settings from .models import UserSession # Provides the arguments "request", "from_session", "to_session" session_client_changed = Signal() def on_user_logged_in(sender, **kwargs): request = kwargs["request"] UserSession.objects.purge_and_list(request.user) UserSession.objects.create_from_request(request) def on_password_changed(sender, **kwargs): if not app_settings.LOGOUT_ON_PASSWORD_CHANGE: request = kwargs["request"] UserSession.objects.create_from_request(request) ================================================ FILE: allauth/usersessions/urls.py ================================================ from django.urls import path from allauth.usersessions import views urlpatterns = [ path("", views.list_usersessions, name="usersessions_list"), ] ================================================ FILE: allauth/usersessions/views.py ================================================ from django.contrib import messages from django.contrib.auth.decorators import login_required from django.http import HttpResponse from django.urls import reverse_lazy from django.utils.decorators import method_decorator from django.views.generic.edit import FormView from allauth.account import app_settings as account_settings from allauth.account.adapter import get_adapter as get_account_adapter from allauth.usersessions import app_settings from allauth.usersessions.forms import ManageUserSessionsForm from allauth.usersessions.models import UserSession @method_decorator(login_required, name="dispatch") class ListUserSessionsView(FormView): template_name = ( f"usersessions/usersession_list.{account_settings.TEMPLATE_EXTENSION}" ) form_class = ManageUserSessionsForm success_url = reverse_lazy("usersessions_list") def get_context_data(self, **kwargs) -> dict: ret = super().get_context_data(**kwargs) sessions = sorted( UserSession.objects.purge_and_list(self.request.user), key=lambda s: s.created_at, ) ret["sessions"] = sessions ret["session_count"] = len(sessions) ret["show_last_seen_at"] = app_settings.TRACK_ACTIVITY return ret def get_form_kwargs(self) -> dict: ret = super().get_form_kwargs() ret["request"] = self.request return ret def form_valid(self, form) -> HttpResponse: form.save(self.request) get_account_adapter().add_message( self.request, messages.INFO, "usersessions/messages/sessions_logged_out.txt", ) return super().form_valid(form) list_usersessions = ListUserSessionsView.as_view() ================================================ FILE: allauth/utils.py ================================================ import importlib import random import re import string import unicodedata from collections import OrderedDict from urllib.parse import urlsplit from django.conf import settings from django.contrib.auth import get_user_model from django.core.exceptions import ImproperlyConfigured, ValidationError from django.http import HttpRequest from django.utils.encoding import force_str from allauth import app_settings from allauth.core import context # Magic number 7: if you run into collisions with this number, then you are # of big enough scale to start investing in a decent user model... MAX_USERNAME_SUFFIX_LENGTH = 7 USERNAME_SUFFIX_CHARS = [string.digits] * 4 + [string.ascii_letters] * ( MAX_USERNAME_SUFFIX_LENGTH - 4 ) def _generate_unique_username_base(txts, regex=None): from .account.adapter import get_adapter adapter = get_adapter() username = None regex = regex or r"[^\w\s@+.-]" for txt in txts: if not txt: continue username = unicodedata.normalize("NFKD", force_str(txt)) username = username.encode("ascii", "ignore").decode("ascii") if len(username) == 0: continue username = force_str(re.sub(regex, "", username).lower()) # Django allows for '@' in usernames in order to accommodate for # project wanting to use email for username. In allauth we don't # use this, we already have a proper place for putting email # addresses (EmailAddress), so let's not use the full email # address and only take the part leading up to the '@'. username = username.split("@")[0] username = username.strip() username = re.sub(r"\s+", "_", username) # Finally, validating base username without database lookups etc. try: username = adapter.clean_username(username, shallow=True) break except ValidationError: pass return username or "user" def get_username_max_length(): from .account.app_settings import USER_MODEL_USERNAME_FIELD if USER_MODEL_USERNAME_FIELD is not None: User = get_user_model() max_length = User._meta.get_field(USER_MODEL_USERNAME_FIELD).max_length else: max_length = 0 return max_length def generate_username_candidate(basename, suffix_length): max_length = get_username_max_length() suffix = "".join( random.choice(USERNAME_SUFFIX_CHARS[i]) for i in range(suffix_length) # nosec ) return basename[0 : max_length - len(suffix)] + suffix def generate_username_candidates(basename): from .account.app_settings import USERNAME_MIN_LENGTH if len(basename) >= USERNAME_MIN_LENGTH: ret = [basename] else: ret = [] min_suffix_length = max(1, USERNAME_MIN_LENGTH - len(basename)) max_suffix_length = min(get_username_max_length(), MAX_USERNAME_SUFFIX_LENGTH) for suffix_length in range(min_suffix_length, max_suffix_length): ret.append(generate_username_candidate(basename, suffix_length)) return ret def generate_unique_username(txts, regex=None): from allauth.account.utils import filter_users_by_username from .account.adapter import get_adapter from .account.app_settings import USER_MODEL_USERNAME_FIELD adapter = get_adapter() basename = _generate_unique_username_base(txts, regex) candidates = generate_username_candidates(basename) existing_usernames = filter_users_by_username(*candidates).values_list( USER_MODEL_USERNAME_FIELD, flat=True ) existing_usernames = {n.lower() for n in existing_usernames} for candidate in candidates: if candidate.lower() not in existing_usernames: try: return adapter.clean_username(candidate, shallow=True) except ValidationError: pass # This really should not happen raise NotImplementedError("Unable to find a unique username") def import_attribute(path): assert isinstance(path, str) # nosec pkg, attr = path.rsplit(".", 1) ret = getattr(importlib.import_module(pkg), attr) return ret def import_callable(path_or_callable): if not callable(path_or_callable): ret = import_attribute(path_or_callable) else: ret = path_or_callable return ret def set_form_field_order(form, field_order): """ This function is a verbatim copy of django.forms.Form.order_fields() to support field ordering below Django 1.9. field_order is a list of field names specifying the order. Append fields not included in the list in the default order for backward compatibility with subclasses not overriding field_order. If field_order is None, keep all fields in the order defined in the class. Ignore unknown fields in field_order to allow disabling fields in form subclasses without redefining ordering. """ if field_order is None: return fields = OrderedDict() for key in field_order: try: fields[key] = form.fields.pop(key) except KeyError: # ignore unknown fields pass fields.update(form.fields) # add remaining fields in original order form.fields = fields def build_absolute_uri( request: HttpRequest | None, location: str, protocol: str | None = None ) -> str: """request.build_absolute_uri() helper Like request.build_absolute_uri, but gracefully handling the case where request is None. """ from .account import app_settings as account_settings if request is None: request = context.request if request is None: if not app_settings.SITES_ENABLED: raise ImproperlyConfigured( "Passing `request=None` requires `sites` to be enabled." ) from django.contrib.sites.models import Site site = Site.objects.get_current() bits = urlsplit(location) if not (bits.scheme and bits.netloc): uri = f"{account_settings.DEFAULT_HTTP_PROTOCOL}://{site.domain}{location}" else: uri = location else: uri = request.build_absolute_uri(location) # NOTE: We only force a protocol if we are instructed to do so # (via the `protocol` parameter, or, if the default is set to # HTTPS. The latter keeps compatibility with the debatable use # case of running your site under both HTTP and HTTPS, where one # would want to make sure HTTPS links end up in password reset # mails even while they were initiated on an HTTP password reset # form. if not protocol and account_settings.DEFAULT_HTTP_PROTOCOL == "https": protocol = account_settings.DEFAULT_HTTP_PROTOCOL # (end NOTE) if protocol: uri = f"{protocol}:{uri.partition(':')[2]}" return uri def get_form_class(forms, form_id, default_form): form_class = forms.get(form_id, default_form) if isinstance(form_class, str): form_class = import_attribute(form_class) return form_class def get_request_param(request, param, default=None): if request is None: return default return request.POST.get(param) or request.GET.get(param, default) def get_setting(name, dflt): getter = getattr( settings, "ALLAUTH_SETTING_GETTER", lambda name, dflt: getattr(settings, name, dflt), ) getter = import_callable(getter) return getter(name, dflt) ================================================ FILE: devenv.nix ================================================ { pkgs, lib, config, inputs, ... }: { packages = [ pkgs.git pkgs.swagger-cli pkgs.twine pkgs.woodpecker-cli pkgs.xmlsec pkgs.gettext pkgs.python312Packages.pyls-flake8 pkgs.python312Packages.pylsp-rope pkgs.python312Packages.python-lsp-server pkgs.python312Packages.xmlsec pkgs.python312Packages.build ]; # https://devenv.sh/languages/ languages.python = { enable = true; package = pkgs.python312; venv.enable = true; venv.requirements = '' -r ${./.}/requirements-dev.txt ''; }; enterShell = '' source $VIRTUAL_ENV/bin/activate ''; } ================================================ FILE: devenv.yaml ================================================ # yaml-language-server: $schema=https://devenv.sh/devenv.schema.json inputs: nixpkgs: url: github:cachix/devenv-nixpkgs/rolling # If you're using non-OSS software, you can set allowUnfree to true. # allowUnfree: true # If you're willing to use a package that's vulnerable # permittedInsecurePackages: # - "openssl-1.1.1w" # If you have more than one devenv you can merge them #imports: # - ./backend ================================================ FILE: docs/Makefile ================================================ # Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-allauth.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-allauth.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/django-allauth" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-allauth" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." ================================================ FILE: docs/account/adapter.rst ================================================ Adapter ======= .. autoclass:: allauth.account.adapter.DefaultAccountAdapter :members: ================================================ FILE: docs/account/advanced.rst ================================================ Advanced Usage ============== Custom User Models ------------------ If you use a custom user model you need to specify what field represents the ``username``, if any. Here, ``username`` really refers to the field representing the nickname that the user uses to login, and not to some unique identifier (possibly including an email address) as is the case for Django's ``AbstractBaseUser.USERNAME_FIELD``. Therefore, if your custom user model does not have a ``username`` field (again, not to be mistaken with an email address or user id), you will need to set ``ACCOUNT_USER_MODEL_USERNAME_FIELD`` to ``None``. This will disable username related functionality in ``allauth``. Remember to also remove username from ``ACCOUNT_SIGNUP_FIELDS`` as by default that is present. Similarly, you will need to set ``ACCOUNT_USER_MODEL_EMAIL_FIELD`` to ``None`` or to the proper field (if other than ``email``). For example, if you want to use a custom user model that has ``email`` as the identifying field, and you don't want to collect usernames, you need the following in your settings.py:: ACCOUNT_USER_MODEL_USERNAME_FIELD = None ACCOUNT_SIGNUP_FIELDS = ['email*', 'password1*', 'password2*'] ACCOUNT_LOGIN_METHODS = {'email'} Creating and Populating User instances -------------------------------------- The following adapter methods can be used to intervene in how User instances are created and populated with data - ``allauth.account.adapter.DefaultAccountAdapter``: - ``is_open_for_signup(self, request)``: The default function returns ``True``. You can override this method by returning ``False`` if you want to disable account signup. - ``new_user(self, request)``: Instantiates a new, empty ``User``. - ``save_user(self, request, user, form)``: Populates and saves the ``User`` instance using information provided in the signup form. - ``populate_username(self, request, user)``: Fills in a valid username, if required and missing. If the username is already present, then it is assumed to be valid (unique). - ``confirm_email(self, request, email_address)``: Marks the email address as confirmed and saves to the db. - ``generate_unique_username(self, txts, regex=None)``: Returns a unique username from the combination of strings present in txts iterable. A regex pattern can be passed to the method to make sure the generated username matches it. Invitations ----------- Invitation handling is not supported, and most likely will not be any time soon. An invitation app could cover anything ranging from invitations of new users, to invitations of existing users to participate in restricted parts of the site. All in all, the scope of invitation handling is large enough to warrant being addressed in an app of its own. Still, everything is in place to easily hook up any third party invitation app. The account adapter (``allauth.account.adapter.DefaultAccountAdapter``) offers the following methods: - ``is_open_for_signup(self, request)``. You can override this method to, for example, inspect the session to check if an invitation was accepted. - ``stash_verified_email(self, request, email)``. If an invitation was accepted by following a link in an email, then there is no need to send email verification mails after the signup is completed. Use this method to record the fact that an email address was verified. Custom Redirects ---------------- If redirecting to statically configurable URLs (as specified in your project settings) is not flexible enough, then you can override the following adapter methods: - ``allauth.account.adapter.DefaultAccountAdapter``: - ``get_login_redirect_url(self, request)`` - ``get_logout_redirect_url(self, request)`` - ``get_email_verification_redirect_url(self, email_address)`` - ``get_signup_redirect_url(self, request)`` For example, redirecting to ``/accounts//`` can be implemented as follows:: # project/settings.py: ACCOUNT_ADAPTER = 'project.users.adapter.MyAccountAdapter' # project/users/adapter.py: from django.conf import settings from allauth.account.adapter import DefaultAccountAdapter class MyAccountAdapter(DefaultAccountAdapter): def get_login_redirect_url(self, request): path = "/accounts/{username}/" return path.format(username=request.user.username) ================================================ FILE: docs/account/configuration.rst ================================================ Configuration ============= Overall ******* ``ACCOUNT_ADAPTER`` (default: ``"allauth.account.adapter.DefaultAccountAdapter"``) Specifies the adapter class to use, allowing you to alter certain default behaviour. ``ACCOUNT_FORMS`` Used to override the builtin forms. Defaults to:: ACCOUNT_FORMS = { 'add_email': 'allauth.account.forms.AddEmailForm', 'change_password': 'allauth.account.forms.ChangePasswordForm', 'confirm_login_code': 'allauth.account.forms.ConfirmLoginCodeForm', 'login': 'allauth.account.forms.LoginForm', 'request_login_code': 'allauth.account.forms.RequestLoginCodeForm', 'reset_password': 'allauth.account.forms.ResetPasswordForm', 'reset_password_from_key': 'allauth.account.forms.ResetPasswordKeyForm', 'set_password': 'allauth.account.forms.SetPasswordForm', 'signup': 'allauth.account.forms.SignupForm', 'user_token': 'allauth.account.forms.UserTokenForm', } ``ACCOUNT_PREVENT_ENUMERATION`` (default: ``True``) Controls whether or not information is revealed about whether or not a user account exists. For example, by entering random email addresses in the password reset form you can test whether or not those email addresses are associated with an account. Enabling this setting prevents that, and an email is always sent, regardless of whether or not the account exists. Note that there is a slight usability tax to pay because there is no immediate feedback. Whether or not enumeration can be prevented during signup depends on the email verification method. In case of mandatory verification, enumeration can be properly prevented because the case where an email address is already taken is indistinguishable from the case where it is not. However, in case of optional or disabled email verification, enumeration can only be prevented by allowing the signup to go through, resulting in multiple accounts sharing same email address (although only one of the accounts can ever have it verified). When enumeration prevention is set to ``True``, email address uniqueness takes precedence over enumeration prevention, and the issue of multiple accounts having the same email address will be avoided, thus leaking information. Set it to ``"strict"`` to allow for signups to go through. ``ACCOUNT_RATE_LIMITS`` (default: ``{...}``) In order to be secure out of the box various rate limits are in place. See :doc:`Rate Limits <./rate_limits>` for details. ``ACCOUNT_SESSION_REMEMBER`` (default: ``None``) Controls the life time of the session. Set to ``None`` to ask the user ("Remember me?"), ``False`` to not remember, and ``True`` to always remember. ``ACCOUNT_TEMPLATE_EXTENSION`` (default: ``"html"``) A string defining the template extension to use, defaults to ``html``. Signup ****** ``ACCOUNT_SIGNUP_FIELDS`` (default: ``['username*', 'email', 'password1*', 'password2*']``) The list of fields to complete in the signup form: ``username``, ``email``, ``phone``, ``password1``, ``password2``.. Fields marked with an asterisk (e.g. ``'username*'``) are required. To let the user type in their email address twice to avoid typos, you can add ``'email2'``. The field ``'password2'`` can be used let the user type in their password twice to avoid typos. ``ACCOUNT_SIGNUP_FORM_CLASS`` (default: ``None``) A string pointing to a custom form class (e.g. ``'myapp.forms.SignupForm'``) that is used during signup to ask the user for additional input (e.g. a newsletter signup checkbox, or birth date). This class should derive from just ``forms.Form`` and only list the additional fields you need. It must implement a ``def signup(self, request, user)`` method, which is called during the signup process. This method allows you to handle and store the submitted data as needed. ``ACCOUNT_SIGNUP_FORM_HONEYPOT_FIELD`` (default: ``None``) A string value that will be used as the HTML 'name' property on a honeypot input field on the sign up form. Honeypot fields are hidden to normal users but might be filled out by naive spam bots. When the field is filled out the app will not create a new user and attempt to fool the bot with a fake successful response. We recommend setting this to some believable value that your app does not actually collect on signup e.g. 'phone_number' or 'address'. Honeypots are not always successful for sophisticated bots so this should be used as one layer in a suite of spam detection tools if your site is having trouble with spam. Login ***** ``ACCOUNT_LOGIN_BY_CODE_ENABLED`` (default: ``False``) "Login by email" offers an alternative method of logging in. Instead of entering an email address and accompanying password, the user only enters the email address. Then, a one-time code is sent to that email address which allows the user to login. This method is often referred to as "Magic Code Login". This setting controls whether or not this method of logging in is enabled. ``ACCOUNT_LOGIN_BY_CODE_TRUST_ENABLED`` (default: ``False``) Indicates whether the MFA "Trust this browser?" functionality is to be enabled for logging in by code. Note that this requires the MFA app to be installed. ``ACCOUNT_LOGIN_BY_CODE_MAX_ATTEMPTS`` (default: ``3``) This setting controls the maximum number of attempts the user has at inputting a valid code. ``ACCOUNT_LOGIN_BY_CODE_REQUIRED`` (default: ``False``) When enabled (in case of ``True``), every user logging in is required to input a login confirmation code sent by email. Alternatively, you can specify a set of authentication methods (``"password"``, ``"mfa"``, or ``"socialaccount"``) for which login codes are required. ``ACCOUNT_LOGIN_BY_CODE_SUPPORTS_RESEND`` (default: ``False``) Whether or not the user can request a new login code. ``ACCOUNT_LOGIN_BY_CODE_TIMEOUT`` (default: ``180``) The code that is emailed has a limited life span. It expires this many seconds after which it was sent. ``ACCOUNT_LOGIN_BY_CODE_FORMAT`` (default: ``settings.ALLAUTH_USER_CODE_FORMAT``) Controls the format of the login code. ``ACCOUNT_LOGIN_METHODS`` (default: ``{"username"}``, options: ``"email"`` or ``"username"``) Specifies the login method to use -- whether the user logs in by entering their username, email address, or either one of both. Note that the login methods need to align with ``ACCOUNT_SIGNUP_FIELDS``, as specifying a login method that you cannot sign up with typically points to a configuration error. ``ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION`` (default: ``False``) The default behavior is not log users in and to redirect them to ``ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL``. By changing this setting to ``True``, users will automatically be logged in once they confirm their email address. Note however that this only works when confirming the email address **immediately after signing up**, assuming users didn't close their browser or used some sort of private browsing mode. Note that this setting only affects email verification by link. It has no affect in case you turn on code based verification (``ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED``). ``ACCOUNT_LOGIN_ON_PASSWORD_RESET`` (default: ``False``) By changing this setting to ``True``, users will automatically be logged in once they have reset their password. By default they are redirected to the password reset done page. ``ACCOUNT_LOGIN_TIMEOUT`` (default: ``900``) The maximum allowed time (in seconds) for a login to go through the various login stages. This limits, for example, the time span that the 2FA stage remains available. Logout ****** ``ACCOUNT_LOGOUT_ON_GET`` (default: ``False``) Determines whether or not the user is automatically logged out by a GET request. `GET is not designed to modify the server state `_, and in this case it can be dangerous. See `LogoutView in the documentation `_ for details. ``ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE`` (default: ``False``) Determines whether or not the user is automatically logged out after changing or setting their password. See documentation for `Django's session invalidation on password change `_. Password Reset ************** ``ACCOUNT_PASSWORD_INPUT_RENDER_VALUE`` (default: ``False``) ``render_value`` parameter as passed to ``PasswordInput`` fields. ``ACCOUNT_PASSWORD_RESET_BY_CODE_ENABLED`` (default: ``False``) Controls whether password reset is performed by means of following a link in the email (``False``), or by entering a code (``True``). ``ACCOUNT_PASSWORD_RESET_BY_CODE_FORMAT`` (default: ``settings.ALLAUTH_USER_CODE_FORMAT``) Controls the format of the password reset code. ``ACCOUNT_PASSWORD_RESET_BY_CODE_MAX_ATTEMPTS`` (default: ``3``) This setting controls the maximum number of attempts the user has at inputting a valid code. ``ACCOUNT_PASSWORD_RESET_BY_CODE_TIMEOUT`` (default: ``180``) The code that is emailed has a limited life span. It expires this many seconds after which it was sent. ``ACCOUNT_PASSWORD_RESET_TOKEN_GENERATOR`` (default: ``"allauth.account.forms.EmailAwarePasswordResetTokenGenerator"``) A string pointing to a custom token generator (e.g. 'myapp.auth.CustomTokenGenerator') for password resets. This class should implement the same methods as ``django.contrib.auth.tokens.PasswordResetTokenGenerator`` or subclass it. Email Verification ****************** ``ACCOUNT_CONFIRM_EMAIL_ON_GET`` (default: ``False``) Determines whether or not an email address is automatically confirmed by a GET request. `GET is not designed to modify the server state `_, though it is commonly used for email confirmation. To avoid requiring user interaction, consider using POST via Javascript in your email confirmation template as an alternative to setting this to True. ``ACCOUNT_EMAIL_CONFIRMATION_HMAC`` (default: ``True``) In order to verify an email address, a key is mailed identifying the email address to be verified. In previous versions, a record was stored in the database for each ongoing email confirmation, keeping track of these keys. Current versions use HMAC based keys that do not require server side state. ``ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS`` (default: ``3``) Determines the expiration date of email confirmation mails (# of days). ``ACCOUNT_EMAIL_VERIFICATION`` (default: ``"optional"``) Determines the email verification method during signup -- choose one of ``"mandatory"``, ``"optional"``, or ``"none"``. When set to ``"mandatory"`` the user is blocked from logging in until the email address is verified. Choose ``"optional"`` or ``"none"`` to allow logins with an unverified email address. In case of ``"optional"``, the email verification mail is still sent, whereas in case of "none" no email verification mails are sent. Setting this to ``"mandatory"`` requires ``"email*"`` to be listed in ``ACCOUNT_SIGNUP_FIELDS``. ``ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED`` (default: ``False``) Controls whether email verification is performed by means of following a link in the email (``False``), or by entering a code (``True``). ``ACCOUNT_EMAIL_VERIFICATION_BY_CODE_FORMAT`` (default: ``settings.ALLAUTH_USER_CODE_FORMAT``) Controls the format of the email verification code. ``ACCOUNT_EMAIL_VERIFICATION_BY_CODE_MAX_ATTEMPTS`` (default: ``3``) This setting controls the maximum number of attempts the user has at inputting a valid code. ``ACCOUNT_EMAIL_VERIFICATION_BY_CODE_TIMEOUT`` (default: ``900``) The code that is emailed has a limited life span. It expires this many seconds after which it was sent. ``ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_CHANGE`` (default: ``False``) Whether or not the email can be changed after signup at the email verification stage. **Warning**: If enumeration prevention is turned on, no account is created when a user signs up using an already existing email. If the user then were able to change to a new email address that is not taken, we would have to create an account as we did not do so yet. Currently, this is not implemented. Changing email address in this scenario is simply not allowed, and therefore, a user paying close attention can deduce that an account already exists. This will be revisited in a future release. ``ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_RESEND`` (default: ``False``) Whether or not the user can request a new email verification code. Reauthentication **************** ``ACCOUNT_REAUTHENTICATION_TIMEOUT`` (default: ``300``) Before asking the user to reauthenticate, we check if a successful (re)authentication happened within the amount of seconds specified here, and if that is the case, the new reauthentication flow is silently skipped. ``ACCOUNT_REAUTHENTICATION_REQUIRED`` (default: ``False``) Specifies whether or not reauthentication is required before the user can alter his account. Routing ******* ``ACCOUNT_AUTHENTICATED_LOGIN_REDIRECTS`` (default: ``True``) The default behaviour is to redirect authenticated users to ``LOGIN_REDIRECT_URL`` when they try accessing login/signup pages. By changing this setting to ``False``, logged in users will not be redirected when they access login/signup pages. ``ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL`` (default: ``settings.LOGIN_URL``) The URL to redirect to after a successful email confirmation, in case no user is logged in. ``ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL`` (default: ``None``) The URL to redirect to after a successful email confirmation, in case of an authenticated user. Set to ``None`` to use ``settings.LOGIN_REDIRECT_URL``. ``ACCOUNT_LOGOUT_REDIRECT_URL`` (default: ``settings.LOGOUT_REDIRECT_URL or "/"``) The URL (or URL name) to return to after the user logs out. Defaults to Django's ``LOGOUT_REDIRECT_URL``, unless that is empty, then ``"/"`` is used. ``ACCOUNT_SIGNUP_REDIRECT_URL`` (default: ``settings.LOGIN_REDIRECT_URL``) The URL (or URL name) to redirect to directly after signing up. Note that users are only redirected to this URL if the signup went through uninterruptedly, for example, without any side steps due to email verification. If your project requires the user to always pass through certain onboarding views after signup, you will have to keep track of state indicating whether or not the user successfully onboarded, and handle accordingly. Sending Email ************* ``ACCOUNT_EMAIL_SUBJECT_PREFIX`` (default: ``"[Site] "``) Subject-line prefix to use for email messages sent. By default, the name of the current ``Site`` (``django.contrib.sites``) is used. ``ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS`` (default: ``True``) Configures whether password reset attempts for email addresses which do not have an account result in sending an email. ``ACCOUNT_EMAIL_NOTIFICATIONS`` (default: ``False``) When enabled, account related security notifications, such as "Your password was changed", including information on user agent / IP address from where the change originated, will be emailed. Email Addresses *************** ``ACCOUNT_CHANGE_EMAIL`` (default: ``False``) When disabled (``False``), users can add one or more email addresses (up to a maximum of ``ACCOUNT_MAX_EMAIL_ADDRESSES``) to their account and freely manage those email addresses. When enabled (``True``), users are limited to having exactly one email address that they can change by adding a temporary second email address that, when verified, replaces the current email address. ``ACCOUNT_EMAIL_MAX_LENGTH`` (default: ``254``) Maximum length of the email field. You won't need to alter this unless using MySQL with the InnoDB storage engine and the ``utf8mb4`` charset, and only in versions lower than 5.7.7, because the default InnoDB settings don't allow indexes bigger than 767 bytes. When using ``utf8mb4``, characters are 4-bytes wide, so at maximum column indexes can be 191 characters long (767/4). Unfortunately Django doesn't allow specifying index lengths, so the solution is to reduce the length in characters of indexed text fields. More information can be found at `MySQL's documentation on converting between 3-byte and 4-byte Unicode character sets `_. ``ACCOUNT_MAX_EMAIL_ADDRESSES`` (default: ``None``) The maximum amount of email addresses a user can associate to his account. It is safe to change this setting for an already running project -- it will not negatively affect users that already exceed the allowed amount. Note that if you set the maximum to 1, users will not be able to change their email address. ``ACCOUNT_UNIQUE_EMAIL`` (default: ``True``) Enforce uniqueness of email addresses. On the database level, this implies that only one user account can have an email address marked as verified. Forms prevent a user from registering with or adding an additional email address if that email address is in use by another account. User Model ********** ``ACCOUNT_PRESERVE_USERNAME_CASING`` (default: ``True``) This setting determines whether the username is stored in lowercase (``False``) or whether its casing is to be preserved (``True``). Note that when casing is preserved, potentially expensive ``__iexact`` lookups are performed when filter on username. For now, the default is set to ``True`` to maintain backwards compatibility. ``ACCOUNT_USERNAME_BLACKLIST`` (default: ``[]``) A list of usernames that can't be used by user. ``ACCOUNT_USER_DISPLAY`` (default: a callable returning ``user.username``) A callable (or string of the form ``'some.module.callable_name'``) that takes a user as its only argument and returns the display name of the user. The default implementation returns ``user.username``. ``ACCOUNT_USER_MODEL_EMAIL_FIELD`` (default: ``"email"``) The name of the field containing the ``email``, if any. See custom user models. ``ACCOUNT_USER_MODEL_USERNAME_FIELD`` (default: ``"username"``) The name of the field containing the ``username``, if any. See custom user models. ``ACCOUNT_USERNAME_MIN_LENGTH`` (default: ``1``) An integer specifying the minimum allowed length of a username. ``ACCOUNT_USERNAME_VALIDATORS`` (default: ``None``) A path (``'some.module.validators.custom_username_validators'``) to a list of custom username validators. If left unset, the validators setup within the user model username field are used. Example:: # In validators.py from django.contrib.auth.validators import ASCIIUsernameValidator custom_username_validators = [ASCIIUsernameValidator()] # In settings.py ACCOUNT_USERNAME_VALIDATORS = 'some.module.validators.custom_username_validators' ================================================ FILE: docs/account/decorators.rst ================================================ Decorators ========== Verified Email Required ------------------------ Even when email verification is not mandatory during signup, there may be circumstances during which you really want to prevent unverified users from proceeding. For this purpose you can use the following decorator:: from allauth.account.decorators import verified_email_required @verified_email_required def verified_users_only_view(request): ... The behavior is as follows: - If the user isn't logged in, it acts identically to the ``login_required`` decorator. - If the user is logged in but has no verified email address, an email verification mail is automatically resent and the user is presented with a page informing them they need to verify their email address. ================================================ FILE: docs/account/email.rst ================================================ Email ===== Case Sensitivity **************** Historically, email addresses started out as case sensitive because the local part (the part before the "@") could represent a case sensitive user name. However, over time, this proved to be a bad idea and the RFCs that succeeded the original were adjusted to move away from treating email addresses as case sensitive. - `RFC 821 `_: The original RFC from 1982 relies on case sensitivity. - `RFC 2821 `_: Released in 2001, obsoletes RFC 821, yet, is still case sensitive. - `RFC 5321 `_: In 2008, the "Local-part" is weakened to "MAY be case-sensitive". - `RFC 6530 `_: In 2012, the following is acknowledged: It has long been the case that the email syntax permits choices about mailbox names that are **unwise in practice** [...]. The most often cited examples involve the use of **case-sensitivity** [...] in mailbox local parts. These deliberately unusual constructions **are permitted** by the protocols, and servers are expected to support them. Although they can provide value in special cases, taking advantage of them **is almost always bad practice** unless the intent is to create some form of **security by obscurity**. To deal with this, previous versions of django-allauth used to store email addresses in their original case, while performing lookups in a case insensitive style. This approach led to subtle bugs in upstream code, and also comes at a performance cost (``__iexact`` lookups). The latter requires case insensitive index support, which not all databases support. Re-evaluating the approach in current times has led to the conclusion that the benefits do not outweigh the costs. Therefore, email addresses are now always stored as lower case. ================================================ FILE: docs/account/forms.rst ================================================ Forms ===== Login ***** *Path*: ``allauth.account.forms.LoginForm`` *Used on*: `account_login `__ view. Example override:: from allauth.account.forms import LoginForm class MyCustomLoginForm(LoginForm): def login(self, *args, **kwargs): # Add your own processing here. # You must return the original result. return super().login(*args, **kwargs) You have access to the following: - ``self.user`` is the User object that is logging in. ``settings.py``:: ACCOUNT_FORMS = {'login': 'mysite.forms.MyCustomLoginForm'} By default, the password field renders a "Forgot your password?" link as the help text. By providing a ``"account/password_reset_help_text.html"`` template you can customize that text. Signup ****** *Path*: ``allauth.account.forms.SignupForm`` *Used on*: `account_signup `__ view. Example override:: from allauth.account.forms import SignupForm class MyCustomSignupForm(SignupForm): def save(self, request): # Ensure you call the parent class's save. # .save() returns a User object. user = super().save(request) # Add your own processing here. # You must return the original result. return user ``settings.py``:: ACCOUNT_FORMS = {'signup': 'mysite.forms.MyCustomSignupForm'} Add Email ********* *Path*: ``allauth.account.forms.AddEmailForm`` *Used on*: `account_email `__ view. Example override:: from allauth.account.forms import AddEmailForm class MyCustomAddEmailForm(AddEmailForm): def save(self, request): # Ensure you call the parent class's save. # .save() returns an allauth.account.models.EmailAddress object. email_address_obj = super().save(request) # Add your own processing here. # You must return the original result. return email_address_obj You have access to the following: - ``self.user`` is the User object that is logged in. ``settings.py``:: ACCOUNT_FORMS = {'add_email': 'mysite.forms.MyCustomAddEmailForm'} Change Password *************** *Path*: ``allauth.account.forms.ChangePasswordForm`` *Used on*: `account_change_password `__ view. Example override:: from allauth.account.forms import ChangePasswordForm class MyCustomChangePasswordForm(ChangePasswordForm): def save(self): # Ensure you call the parent class's save. # .save() does not return anything super().save() # Add your own processing here. You have access to the following: - ``self.user`` is the User object that is logged in. ``settings.py``:: ACCOUNT_FORMS = {'change_password': 'mysite.forms.MyCustomChangePasswordForm'} Set Password ************ *Path*: ``allauth.account.forms.SetPasswordForm`` *Used on*: `account_set_password `__ view. Example override:: from allauth.account.forms import SetPasswordForm class MyCustomSetPasswordForm(SetPasswordForm): def save(self): # Ensure you call the parent class's save. # .save() does not return anything super().save() # Add your own processing here. You have access to the following: - ``self.user`` is the User object that is logged in. ``settings.py``:: ACCOUNT_FORMS = {'set_password': 'mysite.forms.MyCustomSetPasswordForm'} Reset Password ************** *Path*: ``allauth.account.forms.ResetPasswordForm`` *Used on*: `account_reset_password `__ view. Example override:: from allauth.account.forms import ResetPasswordForm class MyCustomResetPasswordForm(ResetPasswordForm): def save(self, request): # Ensure you call the parent class's save. # .save() returns a string containing the email address supplied email_address = super().save(request) # Add your own processing here. # Ensure you return the original result return email_address You have access to the following: - ``self.users`` is a list of all possible User objects with matching email address. ``settings.py``:: ACCOUNT_FORMS = {'reset_password': 'mysite.forms.MyCustomResetPasswordForm'} Reset Password From Key *********************** *Path*: ``allauth.account.forms.ResetPasswordKeyForm`` *Used on*: `account_reset_password `__ view. Example override:: from allauth.account.forms import ResetPasswordKeyForm class MyCustomResetPasswordKeyForm(ResetPasswordKeyForm): def save(self): # Add your own processing here. # Ensure you call the parent class's save. # .save() does not return anything super().save() You have access to the following: - ``self.user`` is the User object. ``settings.py``:: ACCOUNT_FORMS = {'reset_password_from_key': 'mysite.forms.MyCustomResetPasswordKeyForm'} ================================================ FILE: docs/account/index.rst ================================================ Regular Accounts ================ .. toctree:: introduction configuration rate_limits views templates forms decorators signals email phone adapter advanced ================================================ FILE: docs/account/introduction.rst ================================================ Introduction ============ A regular account is a user account, identified by an email address or username, and protected by a password. The ``allauth.account`` app is responsible for managing regular accounts. It supports: - Authentication by username or email. - Registration of new users, with custom signup form support. - Email address management: both a simple change email address flow, and more elaborate flows with multiple email addresses (adding secondary, setting a primary) is supported. - Password forgotten flow. - Changing of the password. - Email address verification flow. ================================================ FILE: docs/account/phone.rst ================================================ Phone ===== Installation ************ In addition to following the overall instructions, pay attention to the following in the ``settings.py`` of your project:: # Make sure that the login methods includes "phone" as a method. ACCOUNT_LOGIN_METHODS = {"phone", "email"} # Add a required phone field to the signup fields. # ACCOUNT_SIGNUP_FIELDS = [ 'phone*', 'email*' # Can be left out if you want to only use 'phone'. ] # You will need to provide methods for storing phone numbers, and # sending SMS messages in a custom adapter. ACCOUNT_ADAPTER = 'project.users.adapter.MyAccountAdapter' Configuration ============= Available settings: ``ACCOUNT_PHONE_VERIFICATION_ENABLED`` (default: ``True``) Whether or not mandatory verification of phone numbers during login/signup takes place. ``ACCOUNT_PHONE_VERIFICATION_MAX_ATTEMPTS`` (default: ``3``) This setting controls the maximum number of attempts the user has at inputting a valid code. ``ACCOUNT_PHONE_VERIFICATION_TIMEOUT`` (default: ``900``) The code that is sent has a limited life span. It expires this many seconds after which it was sent. ``ACCOUNT_PHONE_VERIFICATION_SUPPORTS_CHANGE`` (default: ``False``) Whether or not the phone number can be changed after signup at the phone number verification stage. **Warning**: the warning related to enumeration prevent over at ``ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_CHANGE`` holds here as well. ``ACCOUNT_PHONE_VERIFICATION_SUPPORTS_RESEND`` (default: ``False``) Whether or not the user can request a new phone number verification code. ``ACCOUNT_PHONE_VERIFICATION_CODE_FORMAT`` (default: ``settings.ALLAUTH_USER_CODE_FORMAT``) Controls the format of the verification code. Form Fields *********** For presenting a phone number form field to the user a basic ```` field is used that requires input in E164 format. There are various external projects that offer more elaborate phone number input fields. You can switch over to using the fields provided by those projects, or, tweak the phone number validaton logic, by overriding the following adapter methods: .. autoclass:: allauth.account.adapter.DefaultAccountAdapter .. automethod:: phone_form_field .. automethod:: clean_phone Database Models *************** Out of the box, there are no models provided intended to store the phone numbers of users. It is up to the developer to decide where phone numbers are to be stored, for example, on a custom user model, or, on a separate `Phone` model of its own. Once those the models are setup, the following adapter methods need to be populated so that the models will be used: .. autoclass:: allauth.account.adapter.DefaultAccountAdapter .. automethod:: get_phone .. automethod:: set_phone .. automethod:: set_phone_verified .. automethod:: get_user_by_phone Sending SMS Messages ******************** For sending SMS messages, various external providers and packages are available. You can integrate those by overriding the following adapter method: .. autoclass:: allauth.account.adapter.DefaultAccountAdapter .. automethod:: send_verification_code_sms .. automethod:: send_unknown_account_sms ================================================ FILE: docs/account/rate_limits.rst ================================================ Rate Limits =========== In this section the ratelimits related to the ``allauth.account`` app are documented. Refer to the :doc:`overall rate limit documentation <../common/rate_limits>` for more background information on the mechanism itself. The rate limits are configured through the ``ACCOUNT_RATE_LIMITS`` setting: - Set it to ``False`` to disable all rate limits. - Set it to a dictionary, e.g. ``{"action": "your-rate-limit", ...}`` to use the default configuration but with your specific actions overriden. The following actions are available for configuration: ``"change_password"`` (default: ``"5/m/user"``) Changing the password (for already authenticated users). ``"change_phone"`` (default: ``"1/m/user"``) Changing the phone number. ``"manage_email"`` (default: ``"10/m/user"``) Email management related actions, such as add, remove, change primary. ``"reset_password"`` (default: ``"20/m/ip,5/m/key"``) Requesting a password reset. The email for which the password is to be reset is passed as the key. ``"reauthenticate"`` (default: ``"10/m/user"``) Reauthentication (for users already logged in). ``"reset_password_from_key"`` (default: ``"20/m/ip"``) Password reset (the view the password reset email links to). ``"signup"`` (default: ``"20/m/ip"``) Signups. ``"login"`` (default: ``"30/m/ip"``) Logins. ``"login_failed"`` (default: ``"10/m/ip,5/5m/key"``) Restricts the allowed number of failed login attempts. When exceeded, the user is prohibited from logging in for the remainder of the rate limit. Important: while this protects the allauth login view, it does not :doc:`protect Django's admin login from being brute forced <../common/admin>`. ``"confirm_email"`` (default: ``"1/3m/key"`` (link) or ``"1/10s/key"`` (code)) Users can request email confirmation mails via the email management view, and, implicitly, when logging in with an unverified account. This rate limit prevents users from sending too many of these mails. ================================================ FILE: docs/account/signals.rst ================================================ Signals ======= There are several signals emitted during authentication flows. You can hook to them for your own needs. - ``allauth.account.signals.authentication_step_completed(request, user, method, **kwargs)`` Sent when an authentication step was completed. Note that this does not imply that the user is fully signed in. For example, consider a case where a user authenticates using a password, but still needs to complete the 2FA challenge. - ``allauth.account.signals.user_logged_in(request, user)`` Sent when a user logs in. - ``allauth.account.signals.user_logged_out(request, user)`` Sent when a user logs out. - ``allauth.account.signals.user_signed_up(request, user)`` Sent when a user signs up for an account. This signal is typically followed by a ``user_logged_in``, unless email verification prohibits the user to log in. - ``allauth.account.signals.password_set(request, user)`` Sent when a password has been successfully set for the first time. - ``allauth.account.signals.password_changed(request, user)`` Sent when a password has been successfully changed. - ``allauth.account.signals.password_reset(request, user)`` Sent when a password has been successfully reset. - ``allauth.account.signals.email_confirmed(request, email_address)`` Sent after the email address in the db was updated and set to confirmed. - ``allauth.account.signals.email_confirmation_sent(request, confirmation, signup)`` Sent right after the email confirmation is sent. - ``allauth.account.signals.email_changed(request, user, from_email_address, to_email_address)`` Sent when a primary email address has been changed. - ``allauth.account.signals.email_added(request, user, email_address)`` Sent when a new email address has been added. - ``allauth.account.signals.email_removed(request, user, email_address)`` Sent when an email address has been deleted. ================================================ FILE: docs/account/templates.rst ================================================ Template Tags ============= Use ``user_display`` to render a user name without making assumptions on how the user is represented (e.g. render the username, or first name?):: {% load account %} {% user_display user %} Or, if you need to use in a ``{% blocktrans %}``:: {% load account %} {% user_display user as user_display %} {% blocktrans %}{{ user_display }} has logged in...{% endblocktrans %} Then, override the ``ACCOUNT_USER_DISPLAY`` setting with your project specific user display callable. If you set ``ACCOUNT_USERNAME_REQUIRED = False`` and ``ACCOUNT_USER_MODEL_USERNAME_FIELD = None``, then you can simply display the user.email with {{ user }}:: In case you forgot, your username is {{ user }}. ================================================ FILE: docs/account/views.rst ================================================ Views ===== Login ----- *URL name*: ``account_login`` Users login via the ``allauth.account.views.LoginView`` view over at ``/accounts/login/`` (URL name ``account_login``). When users attempt to login while their account is inactive (``user.is_active``) they are presented with the ``account/account_inactive.html`` template. Signup ------ *URL name*: ``account_signup`` Users sign up via the ``allauth.account.views.SignupView`` view over at ``/accounts/signup/`` (URL name ``account_signup``). Logout ------ *URL name*: ``account_logout`` The logout view (``allauth.account.views.LogoutView``) over at ``/accounts/logout/`` (URL name ``account_logout``) requests for confirmation before logging out. The user is logged out only when the confirmation is received by means of a POST request. If you are wondering why, consider what happens when a malicious user embeds the following image in a post:: For this and more background information on the subject, see: - https://code.djangoproject.com/ticket/15619 - http://stackoverflow.com/questions/3521290/logout-get-or-post If you insist on having logout on GET, then please consider adding a bit of Javascript to automatically turn a click on a logout link into a POST. As a last resort, you can set ``ACCOUNT_LOGOUT_ON_GET`` to ``True``. Password Management ------------------- Authenticated users can manage their password account using the ``allauth.account.views.PasswordSetView`` and ``allauth.account.views.PasswordChangeView`` views, over at ``/accounts/password/set/`` respectively ``/accounts/password/change/`` (URL names ``account_set_password`` and ``account_change_password`` respectively). Users are redirected between these views, according to whether or not they have setup a password (``user.has_usable_password()``). Typically, when users signup via a social provider they will not have a password set. Password Reset -------------- *URL name*: ``account_reset_password`` Users can request a password reset using the ``allauth.account.views.PasswordResetView`` view over at ``/accounts/password/reset/`` (URL name ``account_reset_password``). An email will be sent containing a reset link pointing to ``PasswordResetFromKeyView`` view. Emails Management ----------------- *URL name*: ``account_email`` Users manage the email addresses tied to their account using the ``allauth.account.views.EmailView`` view over at ``/accounts/email/`` (URL name ``account_email``). Here, users can add (and verify) email addresses, remove email addresses, and choose a new primary email address. Email Verification ------------------- Depending on the setting ``ACCOUNT_EMAIL_VERIFICATION``, a verification email is sent pointing to the ``allauth.account.views.ConfirmEmailView`` view. The setting ``ACCOUNT_CONFIRM_EMAIL_ON_GET`` determines whether users have to manually confirm the address by submitting a confirmation form, or whether the address is automatically confirmed by a mere GET request. ================================================ FILE: docs/common/admin.rst ================================================ Admin ===== The Django admin site (``django.contrib.admin``) does not use Django allauth by default. Since Django admin provides a custom login view, it does not go through the normal Django allauth workflow. .. warning:: This limitation means that Django allauth features are not applied to the Django admin site: * The admin login is not protected from being brute forced (``ACCOUNT_RATE_LIMITS``). * Two-factor authentication is not enforced. * Any other custom workflow that overrides the Django allauth adapter's login method will not be applied. An easy workaround for this is to require users to login before going to the Django admin site's login page, by adding this to urls.py (note that the following would need to be applied to every instance of ``AdminSite``): .. code-block:: python from django.contrib import admin from allauth.account.decorators import secure_admin_login admin.autodiscover() admin.site.login = secure_admin_login(admin.site.login) ================================================ FILE: docs/common/configuration.rst ================================================ Configuration ============= Available settings: ``ALLAUTH_DEFAULT_AUTO_FIELD`` Can be set to configure the primary key of all models. For example: ``"hashid_field.HashidAutoField"``. ``ALLAUTH_USER_CODE_FORMAT`` (default: ``{"numeric": False, "dashed": True, length: 8}``) Controls the format of user-facing verification codes (e.g. email verification, phone verification, login codes). ================================================ FILE: docs/common/email.rst ================================================ Sending Email ============= Emails sent (e.g. in case of password forgotten or email confirmation) can be altered by providing your own templates. Templates are named as follows:: account/email/email_confirmation_signup_subject.txt account/email/email_confirmation_signup_message.txt account/email/email_confirmation_subject.txt account/email/email_confirmation_message.txt In case you want to include an HTML representation, add an HTML template as follows:: account/email/email_confirmation_signup_message.html account/email/email_confirmation_message.html The project does not contain any HTML email templates out of the box. When you do provide these yourself, note that both the text and HTML versions of the message are sent. If this does not suit your needs, you can hook up your own custom mechanism by overriding the ``send_mail`` method of the account adapter (``allauth.account.adapter.DefaultAccountAdapter``). ================================================ FILE: docs/common/index.rst ================================================ Common Functionality ==================== .. toctree:: :maxdepth: 1 configuration email templates messages admin rate_limits ================================================ FILE: docs/common/messages.rst ================================================ Messages ======== The Django messages framework (``django.contrib.messages``) is used if it is listed in ``settings.INSTALLED_APPS``. All messages (as in ``django.contrib.messages``) are configurable by overriding their respective template. If you want to disable a message, simply override the message template with a blank one. ================================================ FILE: docs/common/rate_limits.rst ================================================ .. _rate_limits: Rate Limits =========== In order to be secure out of the box various rate limits are in place. The rate limit mechanism is backed by a Django cache. Hence, rate limiting will not work properly if you are using the `DummyCache`. When rate limits are hit the ``429.html`` template is rendered, alternatively, you can configure a custom handler by declaring a ``handler429`` view in your root URLconf. Rate limits are consumed by triggering actions, the full list of which is documented below. Per action, the rate can be configured. The rate itself is an amount, per time unit, per either IP address, user or action-specific key. For example, requesting a password reset is an action that is both limited globally by IP address, as well as per email. Here, the email address used is the specific key. .. danger:: Rate limits rely on accurate client IP address detection to function correctly. However, **it is not possible for django-allauth to reliably determine the client IP address out of the box** because the correct method varies depending on your deployment architecture (direct connections, load balancers, reverse proxies, CDNs, etc.). The ``X-Forwarded-For`` header cannot be used to determine the client IP, as **this header can be trivially spoofed by malicious actors**, allowing them to completely bypass rate limits. To ensure rate limits work correctly: 1. **Review and adjust the rate limit configuration settings** documented below (``ALLAUTH_TRUSTED_PROXY_COUNT``, ``ALLAUTH_TRUSTED_CLIENT_IP_HEADER``) to match your security requirements and deployment architecture. 2. If the settings are not sufficient, **override the account adapter's** ``get_client_ip()`` method to implement custom logic for extracting the real client IP address from the correct header(s) for your specific infrastructure. See the :doc:`adapter documentation <../account/adapter>` for guidance on implementing a custom adapter. Configuration ------------- ``ALLAUTH_TRUSTED_PROXY_COUNT`` (default: ``0``) As the ``X-Forwarded-For`` header can be spoofed, you need to configure the number of proxies that are under your control and hence, can be trusted. The default is 0, meaning, no proxies are trusted. As a result, the ``X-Forwarded-For`` header will be disregarded by default. ``ALLAUTH_TRUSTED_CLIENT_IP_HEADER`` (default: ``None``) If your service is running behind a trusted proxy that sets a custom header containing the client IP address, specify that header name here. The client IP will be extracted from this header instead of ``X-Forwarded-For``. Examples: ``"CF-Connecting-IP"`` (Cloudflare), ``"X-Real-IP"`` (nginx). Implementation Notes -------------------- The builtin rate limitting relies on a cache and uses non-atomic operations, making it vulnerable to race conditions. As a result, users may occasionally bypass the intended rate limit due to concurrent access. However, such race conditions are rare in practice. For example, if the limit is set to 10 requests per minute and a large number of parallel processes attempt to test that limit, you may occasionally observe slight overruns—such as 11 or 12 requests slipping through. Nevertheless, exceeding the limit by a large margin is highly unlikely due to the low probability of many processes entering the critical non-atomic code section simultaneously. Testing ------- Unless the rate limit is disabled or the default limits are increased, you might run intro problems if you're running unit tests that are dependant on funcionalities covered by the rate limits. For example, if you're testing the `confirm_email` functionality in your unit tests and you're testing if the verification email is sent twice after requesting it twice, only one of the emails will be sent. ================================================ FILE: docs/common/templates.rst ================================================ Templates ========= Introduction ------------ The templates that are offered out of the box are intentionally plain and without any styling. We do not want to pick a side in the multitudes of frontend styling options out there, and the look and feel typically should be adjusted to match the branding of your project. Therefore it is recommended that you copy all templates over to your own project and adjust them as you see fit. Having said that, with features such as third party account providers and two-factor authentication, adjusting the templates involves a lot more than just styling a ``login.html`` and a ``signup.html`` template. Therefore, a mechanism is included that allows you to adjust the look and feel of all templates by only overriding a few core templates. This approach allows you to achieve visual results fast, but is of course more limited compared to styling all templates yourself. Overriding the Built-In Templates --------------------------------- The ``allauth`` app includes all templates, and can be found in the `allauth/templates `__ directory. When ``allauth`` is part of your ``INSTALLED_APPS``, and ``"APP_DIRS": True`` is configured, Django will be able to find its templates. As ``DIRS`` is searched before ``APP_DIRS``, overriding the templates involves adding an entry to ``DIRS`` that points to your a project specific template folder, as follows:: from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [ BASE_DIR / "templates" ], "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.template.context_processors.debug", "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", ], }, }, ] If you copy over all templates to your ``BASE_DIR / "templates"`` it should contain these entries (a.o.): - An ``account`` folder containing the templates from the ``allauth.account`` app. - A ``socialaccount`` folder containing the templates from the ``allauth.socialaccount`` app. - A ``mfa`` folder containing the templates from the ``allauth.mfa`` app. - An ``allauth`` folder containing the overall styling templates (see the next section). Styling the Existing Templates ------------------------------ Instead of copying all templates, a mechanism is included that allows you to adjust the look and feel of all templates by only overriding a few core templates. This approach allows you to achieve visual results fast, but is of course more limited compared to styling all templates yourself. Layouts ^^^^^^^ The existing templates use two base page layouts: - The entrance layout: These are all pages where the user is in the process of authenticating, such as the login and signup pages. - The account management layout: The pages where an authenticated user can manage the account, such as changing the email address or password. You can alter these layouts by providing these templates in your own project: ========================================== =========== Template file Description ========================================== =========== allauth/layouts/base.html The overall base template. allauth/layouts/entrance.html The entrance template, extending the base template. allauth/layouts/manage.html The account management template, extending the base template. ========================================== =========== Elements ^^^^^^^^ When rendering e.g. a Bootstrap button you would typically use:: Yet, when a different CSS framework is used other class names apply, and possibly even other markup. Therefore, the built-in templates do not include the above content directly. Instead of referring to tags such `` ) } ================================================ FILE: examples/react-spa/frontend/src/account/ChangePassword.js ================================================ import { useState } from 'react' import FormErrors from '../components/FormErrors' import { changePassword } from '../lib/allauth' import { Navigate } from 'react-router-dom' import { useUser } from '../auth' import Button from '../components/Button' export default function ChangePassword () { const hasCurrentPassword = useUser().has_usable_password const [currentPassword, setCurrentPassword] = useState('') const [newPassword, setNewPassword] = useState('') const [newPassword2, setNewPassword2] = useState('') const [newPassword2Errors, setNewPassword2Errors] = useState([]) const [response, setResponse] = useState({ fetching: false, content: null }) function submit () { if (newPassword !== newPassword2) { setNewPassword2Errors([{ param: 'new_password2', message: 'Password does not match.' }]) return } setNewPassword2Errors([]) setResponse({ ...response, fetching: true }) changePassword({ current_password: currentPassword, new_password: newPassword }).then((resp) => { setResponse((r) => { return { ...r, content: resp } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } if (response.content?.status === 200) { return } return (

    {hasCurrentPassword ? 'Change Password' : 'Set Password'}

    {hasCurrentPassword ? 'Enter your current password, followed by your new password.' : 'You currently have no password set. Enter your (new) password.'}

    {hasCurrentPassword ?
    : null}
    ) } ================================================ FILE: examples/react-spa/frontend/src/account/ConfirmLoginCode.js ================================================ import { useState } from 'react' import FormErrors from '../components/FormErrors' import { confirmLoginCode, Flows } from '../lib/allauth' import { Navigate } from 'react-router-dom' import Button from '../components/Button' import { useAuthStatus } from '../auth' export default function ConfirmLoginCode () { const [, authInfo] = useAuthStatus() const [code, setCode] = useState('') const [response, setResponse] = useState({ fetching: false, content: null }) function submit () { setResponse({ ...response, fetching: true }) confirmLoginCode(code).then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } if (response.content?.status === 409 || authInfo.pendingFlow?.id !== Flows.LOGIN_BY_CODE) { return } return (

    Enter Sign-In Code

    The code expires shortly, so please enter it soon.

    ) } ================================================ FILE: examples/react-spa/frontend/src/account/ConfirmPasswordResetCode.js ================================================ import { useState } from 'react' import FormErrors from '../components/FormErrors' import { getPasswordReset, Flows } from '../lib/allauth' import { Navigate } from 'react-router-dom' import Button from '../components/Button' import { useAuthStatus } from '../auth' export default function ConfirmPasswordResetCode () { const [, authInfo] = useAuthStatus() const [code, setCode] = useState('') const [response, setResponse] = useState({ fetching: false, content: null }) function submit () { setResponse({ ...response, fetching: true }) getPasswordReset(code).then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } if (response.content?.status === 409 || authInfo.pendingFlow?.id !== Flows.PASSWORD_RESET_BY_CODE) { return } else if (response.content?.status === 200) { return } return (

    Enter Password Reset Code

    The code expires shortly, so please enter it soon.

    ) } ================================================ FILE: examples/react-spa/frontend/src/account/Login.js ================================================ import { useState } from 'react' import FormErrors from '../components/FormErrors' import { login } from '../lib/allauth' import { Link } from 'react-router-dom' import { useConfig } from '../auth' import ProviderList from '../socialaccount/ProviderList' import Button from '../components/Button' import WebAuthnLoginButton from '../mfa/WebAuthnLoginButton' export default function Login () { const [email, setEmail] = useState('') const [password, setPassword] = useState('') const [response, setResponse] = useState({ fetching: false, content: null }) const config = useConfig() const hasProviders = config.data.socialaccount?.providers?.length > 0 function submit () { setResponse({ ...response, fetching: true }) login({ email, password }).then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } return (

    Login

    No account? Sign up here.

    Forgot your password?
    {config.data.account.login_by_code_enabled ? Send me a sign-in code : null} Sign in with a passkey {hasProviders ? <>

    Or use a third-party

    : null}
    ) } ================================================ FILE: examples/react-spa/frontend/src/account/Logout.js ================================================ import { useState } from 'react' import { Navigate } from 'react-router-dom' import { logout } from '../lib/allauth' import Button from '../components/Button' export default function Logout () { const [response, setResponse] = useState({ fetching: false, content: null }) function submit () { setResponse({ ...response, fetching: true }) logout().then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } if (response.content) { return } return (

    Logout

    Are you sure you want to logout?

    ) } ================================================ FILE: examples/react-spa/frontend/src/account/Reauthenticate.js ================================================ import { useState } from 'react' import FormErrors from '../components/FormErrors' import { reauthenticate, Flows } from '../lib/allauth' import ReauthenticateFlow from './ReauthenticateFlow' import Button from '../components/Button' export default function Reauthenticate () { const [password, setPassword] = useState('') const [response, setResponse] = useState({ fetching: false, content: null }) function submit () { setResponse({ ...response, fetching: true }) reauthenticate({ password }).then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } return (

    Enter your password:

    ) } ================================================ FILE: examples/react-spa/frontend/src/account/ReauthenticateFlow.js ================================================ import { Link, useLocation } from 'react-router-dom' import { pathForFlow } from '../auth' import { Flows, AuthenticatorType } from '../lib/allauth' const flowLabels = {} flowLabels[Flows.REAUTHENTICATE] = 'Use your password' flowLabels[`${Flows.MFA_REAUTHENTICATE}:${AuthenticatorType.TOTP}`] = 'Use your authenticator app' flowLabels[`${Flows.MFA_REAUTHENTICATE}:${AuthenticatorType.RECOVERY_CODES}`] = 'Use a recovery code' flowLabels[`${Flows.MFA_REAUTHENTICATE}:${AuthenticatorType.WEBAUTHN}`] = 'Use security key' function flowsToMethods (flows) { const methods = [] flows.forEach(flow => { if (flow.id === Flows.MFA_REAUTHENTICATE) { flow.types.forEach(typ => { const id = `${flow.id}:${typ}` methods.push({ label: flowLabels[id], id, path: pathForFlow(flow, typ) }) }) } else { methods.push({ label: flowLabels[flow.id] || flow.id, id: flow.id, path: pathForFlow(flow) }) } }) return methods } export default function ReauthenticateFlow (props) { const location = useLocation() const methods = flowsToMethods(location.state.reauth.data.flows) return (

    Confirm Access

    Please reauthenticate to safeguard your account.

    {props.children} {methods.length > 1 ? <>

    Alternative Options

      {methods.filter(method => method.id !== props.method).map(method => { return (
    • {method.label}
    • ) })}
    : null}
    ) } ================================================ FILE: examples/react-spa/frontend/src/account/RequestLoginCode.js ================================================ import { useState } from 'react' import FormErrors from '../components/FormErrors' import { requestLoginCode } from '../lib/allauth' import { Navigate } from 'react-router-dom' import Button from '../components/Button' export default function RequestLoginCode () { const [email, setEmail] = useState('') const [response, setResponse] = useState({ fetching: false, content: null }) function submit () { setResponse({ ...response, fetching: true }) requestLoginCode(email).then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } if (response.content?.status === 401) { return } return (

    Send me a sign-in code

    You will receive an email containing a special code for a password-free sign-in.

    ) } ================================================ FILE: examples/react-spa/frontend/src/account/RequestPasswordReset.js ================================================ import { useState } from 'react' import FormErrors from '../components/FormErrors' import { requestPasswordReset, Flows } from '../lib/allauth' import { Navigate, Link } from 'react-router-dom' import Button from '../components/Button' export default function RequestPasswordReset () { const [email, setEmail] = useState('') const [response, setResponse] = useState({ fetching: false, content: null }) function submit () { setResponse({ ...response, fetching: true }) requestPasswordReset(email).then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } if (response.content?.status === 401) { return } if (response.content?.status === 200) { return (

    Reset Password

    Password reset sent.

    ) } return (

    Reset Password

    Remember your password? Back to login.

    ) } ================================================ FILE: examples/react-spa/frontend/src/account/ResetPassword.js ================================================ import { useState } from 'react' import FormErrors from '../components/FormErrors' import { getPasswordReset, resetPassword } from '../lib/allauth' import { Navigate, Link, useLocation, useLoaderData } from 'react-router-dom' import Button from '../components/Button' export async function resetPasswordByLinkLoader ({ params }) { const key = params.key const resp = await getPasswordReset(key) return { resetKey: key, resetKeyResponse: resp } } function ResetPassword ({ resetKey, resetKeyResponse }) { const [password1, setPassword1] = useState('') const [password2, setPassword2] = useState('') const [password2Errors, setPassword2Errors] = useState([]) const [response, setResponse] = useState({ fetching: false, content: null }) function submit () { if (password2 !== password1) { setPassword2Errors([{ param: 'password2', message: 'Password does not match.' }]) return } setPassword2Errors([]) setResponse({ ...response, fetching: true }) resetPassword({ key: resetKey, password: password1 }).then((resp) => { setResponse((r) => { return { ...r, content: resp } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } if ([200, 401].includes(response.content?.status)) { return } let body if (resetKeyResponse.status !== 200) { body = } else if (response.content?.errors?.filter(e => e.param === 'key')) { body = } else { body = ( <>
    ) } return (

    Reset Password

    Remember your password? Back to login.

    {body}
    ) } export function ResetPasswordByLink () { const { resetKey, resetKeyResponse } = useLoaderData() return } export function ResetPasswordByCode () { const { state } = useLocation() if (!state || !state.resetKey || !state.resetKeyResponse) { return } return } ================================================ FILE: examples/react-spa/frontend/src/account/Signup.js ================================================ import { useState } from 'react' import FormErrors from '../components/FormErrors' import { signUp } from '../lib/allauth' import { Link } from 'react-router-dom' import { useConfig } from '../auth' import ProviderList from '../socialaccount/ProviderList' import Button from '../components/Button' export default function Signup () { const [email, setEmail] = useState('') const [password1, setPassword1] = useState('') const [password2, setPassword2] = useState('') const [password2Errors, setPassword2Errors] = useState([]) const [response, setResponse] = useState({ fetching: false, content: null }) const config = useConfig() const hasProviders = config.data.socialaccount?.providers?.length > 0 function submit () { if (password2 !== password1) { setPassword2Errors([{ param: 'password2', message: 'Password does not match.' }]) return } setPassword2Errors([]) setResponse({ ...response, fetching: true }) signUp({ email, password: password1 }).then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } return (

    Sign Up

    Already have an account? Login here.

    Sign up using a passkey {hasProviders ? <>

    Or use a third-party

    : null}
    ) } ================================================ FILE: examples/react-spa/frontend/src/account/VerificationEmailSent.js ================================================ export default function VerificationEmailSent () { return (

    Confirm Email Address

    Please confirm your email address.

    ) } ================================================ FILE: examples/react-spa/frontend/src/account/VerifyEmail.js ================================================ import { useState } from 'react' import { useLoaderData, Navigate } from 'react-router-dom' import { getEmailVerification, verifyEmail } from '../lib/allauth' import Button from '../components/Button' export async function loader ({ params }) { const key = params.key const resp = await getEmailVerification(key) return { key, verification: resp } } export default function VerifyEmail () { const { key, verification } = useLoaderData() const [response, setResponse] = useState({ fetching: false, content: null }) function submit () { setResponse({ ...response, fetching: true }) verifyEmail(key).then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } if ([200, 401].includes(response.content?.status)) { return } let body = null if (verification.status === 200) { body = ( <>

    Please confirm that {verification.data.email} is an email address for user {verification.data.user.str}.

    ) } else if (!verification.data?.email) { body =

    Invalid verification link.

    } else { body =

    Unable to confirm email {verification.data.email} because it is already confirmed.

    } return (

    Confirm Email Address

    {body}
    ) } ================================================ FILE: examples/react-spa/frontend/src/account/VerifyEmailByCode.js ================================================ import { useState } from 'react' import FormErrors from '../components/FormErrors' import { Navigate } from 'react-router-dom' import { verifyEmail } from '../lib/allauth' import Button from '../components/Button' export default function VerifyEmail () { const [code, setCode] = useState('') const [response, setResponse] = useState({ fetching: false, content: null }) function submit () { setResponse({ ...response, fetching: true }) verifyEmail(code).then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } if ([200, 401].includes(response.content?.status)) { return } return (

    Confirm Email Address

    ) } ================================================ FILE: examples/react-spa/frontend/src/auth/AuthContext.js ================================================ import { useEffect, createContext, useState } from 'react' import { getAuth, getConfig } from '../lib/allauth' export const AuthContext = createContext(null) function Loading () { return
    Starting...
    } function LoadingError () { return
    Loading error!
    } export function AuthContextProvider (props) { const [auth, setAuth] = useState(undefined) const [config, setConfig] = useState(undefined) useEffect(() => { function onAuthChanged (e) { setAuth(auth => { if (typeof auth === 'undefined') { console.log('Authentication status loaded') } else { console.log('Authentication status updated') } return e.detail } ) } document.addEventListener('allauth.auth.change', onAuthChanged) getAuth().then(data => setAuth(data)).catch((e) => { console.error(e) setAuth(false) }) getConfig().then(data => setConfig(data)).catch((e) => { console.error(e) }) return () => { document.removeEventListener('allauth.auth.change', onAuthChanged) } }, []) const loading = (typeof auth === 'undefined') || config?.status !== 200 return ( {loading ? : (auth === false ? : props.children)} ) } ================================================ FILE: examples/react-spa/frontend/src/auth/hooks.js ================================================ import { useContext, useRef, useState, useEffect } from 'react' import { AuthContext } from './AuthContext' export function useAuth () { return useContext(AuthContext)?.auth } export function useConfig () { return useContext(AuthContext)?.config } export function useUser () { const auth = useContext(AuthContext)?.auth return authInfo(auth).user } export function useAuthInfo () { const auth = useContext(AuthContext)?.auth return authInfo(auth) } function authInfo (auth) { const isAuthenticated = auth.status === 200 || (auth.status === 401 && auth.meta.is_authenticated) const requiresReauthentication = isAuthenticated && auth.status === 401 const pendingFlow = auth.data?.flows?.find(flow => flow.is_pending) return { isAuthenticated, requiresReauthentication, user: isAuthenticated ? auth.data.user : null, pendingFlow } } export const AuthChangeEvent = Object.freeze({ LOGGED_OUT: 'LOGGED_OUT', LOGGED_IN: 'LOGGED_IN', REAUTHENTICATED: 'REAUTHENTICATED', REAUTHENTICATION_REQUIRED: 'REAUTHENTICATION_REQUIRED', FLOW_UPDATED: 'FLOW_UPDATED' }) function determineAuthChangeEvent (fromAuth, toAuth) { let fromInfo = authInfo(fromAuth) const toInfo = authInfo(toAuth) if (toAuth.status === 410) { return AuthChangeEvent.LOGGED_OUT } // Corner case: user ID change. Treat as if we're transitioning from anonymous state. if (fromInfo.user && toInfo.user && fromInfo.user?.id !== toInfo.user?.id) { fromInfo = { isAuthenticated: false, requiresReauthentication: false, user: null } } if (!fromInfo.isAuthenticated && toInfo.isAuthenticated) { // You typically don't transition from logged out to reauthentication required. return AuthChangeEvent.LOGGED_IN } else if (fromInfo.isAuthenticated && !toInfo.isAuthenticated) { return AuthChangeEvent.LOGGED_OUT } else if (fromInfo.isAuthenticated && toInfo.isAuthenticated) { if (toInfo.requiresReauthentication) { return AuthChangeEvent.REAUTHENTICATION_REQUIRED } else if (fromInfo.requiresReauthentication) { return AuthChangeEvent.REAUTHENTICATED } else if (fromAuth.data.methods.length < toAuth.data.methods.length) { // If you do a page reload when on the reauthentication page, both fromAuth // and toAuth are authenticated, and it won't see the change when // reauthentication without this. return AuthChangeEvent.REAUTHENTICATED } } else if (!fromInfo.isAuthenticated && !toInfo.isAuthenticated) { const fromFlow = fromInfo.pendingFlow const toFlow = toInfo.pendingFlow if (toFlow?.id && fromFlow?.id !== toFlow.id) { return AuthChangeEvent.FLOW_UPDATED } } // No change. return null } export function useAuthChange () { const auth = useAuth() const ref = useRef({ prevAuth: auth, event: null, didChange: false }) const [, setForcedUpdate] = useState(0) useEffect(() => { if (ref.current.prevAuth) { ref.current.didChange = true const event = determineAuthChangeEvent(ref.current.prevAuth, auth) if (event) { ref.current.event = event setForcedUpdate(gen => gen + 1) } } ref.current.prevAuth = auth }, [auth, ref]) const didChange = ref.current.didChange if (didChange) { ref.current.didChange = false } const event = ref.current.event if (event) { ref.current.event = null } return [auth, event] } export function useAuthStatus () { const auth = useAuth() return [auth, authInfo(auth)] } ================================================ FILE: examples/react-spa/frontend/src/auth/index.js ================================================ export { AuthContextProvider } from './AuthContext' export { URLs, pathForPendingFlow, pathForFlow, AuthChangeRedirector, AuthenticatedRoute, AnonymousRoute } from './routing' export { useConfig, useAuth, useUser, useAuthStatus } from './hooks' ================================================ FILE: examples/react-spa/frontend/src/auth/routing.js ================================================ import { Navigate, useLocation } from 'react-router-dom' import { useAuthChange, AuthChangeEvent, useAuthStatus } from './hooks' import { Flows, AuthenticatorType } from '../lib/allauth' export const URLs = Object.freeze({ LOGIN_URL: '/account/login', LOGIN_REDIRECT_URL: '/calculator', LOGOUT_REDIRECT_URL: '/' }) const flow2path = {} flow2path[Flows.LOGIN] = '/account/login' flow2path[Flows.LOGIN_BY_CODE] = '/account/login/code/confirm' flow2path[Flows.SIGNUP] = '/account/signup' flow2path[Flows.VERIFY_EMAIL] = '/account/verify-email' flow2path[Flows.PASSWORD_RESET_BY_CODE] = '/account/password/reset/confirm' flow2path[Flows.PROVIDER_SIGNUP] = '/account/provider/signup' flow2path[Flows.REAUTHENTICATE] = '/account/reauthenticate' flow2path[Flows.MFA_TRUST] = '/account/2fa/trust' flow2path[`${Flows.MFA_AUTHENTICATE}:${AuthenticatorType.TOTP}`] = '/account/authenticate/totp' flow2path[`${Flows.MFA_AUTHENTICATE}:${AuthenticatorType.RECOVERY_CODES}`] = '/account/authenticate/recovery-codes' flow2path[`${Flows.MFA_AUTHENTICATE}:${AuthenticatorType.WEBAUTHN}`] = '/account/authenticate/webauthn' flow2path[`${Flows.MFA_REAUTHENTICATE}:${AuthenticatorType.TOTP}`] = '/account/reauthenticate/totp' flow2path[`${Flows.MFA_REAUTHENTICATE}:${AuthenticatorType.RECOVERY_CODES}`] = '/account/reauthenticate/recovery-codes' flow2path[`${Flows.MFA_REAUTHENTICATE}:${AuthenticatorType.WEBAUTHN}`] = '/account/reauthenticate/webauthn' flow2path[Flows.MFA_WEBAUTHN_SIGNUP] = '/account/signup/passkey/create' export function pathForFlow (flow, typ) { let key = flow.id if (typeof flow.types !== 'undefined') { typ = typ ?? flow.types[0] key = `${key}:${typ}` } const path = flow2path[key] ?? flow2path[flow.id] if (!path) { throw new Error(`Unknown path for flow: ${flow.id}`) } return path } export function pathForPendingFlow (auth) { const flow = auth.data.flows.find(flow => flow.is_pending) if (flow) { return pathForFlow(flow) } return null } function navigateToPendingFlow (auth) { const path = pathForPendingFlow(auth) if (path) { return } return null } export function AuthenticatedRoute ({ children }) { const location = useLocation() const [, status] = useAuthStatus() const next = `next=${encodeURIComponent(location.pathname + location.search)}` if (status.isAuthenticated) { return children } else { return } } export function AnonymousRoute ({ children }) { const [, status] = useAuthStatus() if (!status.isAuthenticated) { return children } else { return } } export function AuthChangeRedirector ({ children }) { const [auth, event] = useAuthChange() const location = useLocation() switch (event) { case AuthChangeEvent.LOGGED_OUT: return case AuthChangeEvent.LOGGED_IN: return case AuthChangeEvent.REAUTHENTICATED: { const next = new URLSearchParams(location.search).get('next') || '/' return } case AuthChangeEvent.REAUTHENTICATION_REQUIRED: { const next = `next=${encodeURIComponent(location.pathname + location.search)}` const path = pathForFlow(auth.data.flows[0]) return } case AuthChangeEvent.FLOW_UPDATED: const pendingFlow = navigateToPendingFlow(auth) if (!pendingFlow) { throw new Error() } return pendingFlow default: break } // ...stay where we are return children } ================================================ FILE: examples/react-spa/frontend/src/components/Button.js ================================================ export default function Button (props) { return } ================================================ FILE: examples/react-spa/frontend/src/components/FormErrors.js ================================================ export default function FormErrors (props) { if (!props.errors || !props.errors.length) { return null } const errors = props.errors.filter(error => (props.param ? error.param === props.param : error.param == null)) if (!errors.length) { return null } return
      {errors.map((e, i) =>
    • {e.message}
    • )}
    } ================================================ FILE: examples/react-spa/frontend/src/index.js ================================================ import React from 'react' import ReactDOM from 'react-dom/client' import App from './App' import { init } from './init' init() const root = ReactDOM.createRoot(document.getElementById('root')) root.render( ) ================================================ FILE: examples/react-spa/frontend/src/init.js ================================================ import { setup } from './lib/allauth' export function init () { if (document.location.hostname === 'app.react.demo.allauth.org') { setup('app', 'https://api.react.demo.allauth.org/_allauth/app/v1', false) } else if (document.location.hostname === 'react.demo.allauth.org') { setup('browser', 'https://api.react.demo.allauth.org/_allauth/browser/v1', true) } } ================================================ FILE: examples/react-spa/frontend/src/lib/allauth.js ================================================ import { getCSRFToken } from './django' export const Client = Object.freeze({ APP: 'app', BROWSER: 'browser' }) export const settings = { client: Client.BROWSER, baseUrl: `/_allauth/${Client.BROWSER}/v1`, withCredentials: false } const ACCEPT_JSON = { accept: 'application/json' } export const AuthProcess = Object.freeze({ LOGIN: 'login', CONNECT: 'connect' }) export const Flows = Object.freeze({ LOGIN: 'login', LOGIN_BY_CODE: 'login_by_code', MFA_AUTHENTICATE: 'mfa_authenticate', MFA_REAUTHENTICATE: 'mfa_reauthenticate', MFA_TRUST: 'mfa_trust', MFA_WEBAUTHN_SIGNUP: 'mfa_signup_webauthn', PASSWORD_RESET_BY_CODE: 'password_reset_by_code', PROVIDER_REDIRECT: 'provider_redirect', PROVIDER_SIGNUP: 'provider_signup', REAUTHENTICATE: 'reauthenticate', SIGNUP: 'signup', VERIFY_EMAIL: 'verify_email', }) export const URLs = Object.freeze({ // Meta CONFIG: '/config', // Account management CHANGE_PASSWORD: '/account/password/change', EMAIL: '/account/email', PROVIDERS: '/account/providers', // Account management: 2FA AUTHENTICATORS: '/account/authenticators', RECOVERY_CODES: '/account/authenticators/recovery-codes', TOTP_AUTHENTICATOR: '/account/authenticators/totp', // Auth: Basics LOGIN: '/auth/login', REQUEST_LOGIN_CODE: '/auth/code/request', CONFIRM_LOGIN_CODE: '/auth/code/confirm', SESSION: '/auth/session', REAUTHENTICATE: '/auth/reauthenticate', REQUEST_PASSWORD_RESET: '/auth/password/request', RESET_PASSWORD: '/auth/password/reset', SIGNUP: '/auth/signup', VERIFY_EMAIL: '/auth/email/verify', // Auth: 2FA MFA_AUTHENTICATE: '/auth/2fa/authenticate', MFA_REAUTHENTICATE: '/auth/2fa/reauthenticate', MFA_TRUST: '/auth/2fa/trust', // Auth: Social PROVIDER_SIGNUP: '/auth/provider/signup', REDIRECT_TO_PROVIDER: '/auth/provider/redirect', PROVIDER_TOKEN: '/auth/provider/token', // Auth: Sessions SESSIONS: '/auth/sessions', // Auth: WebAuthn REAUTHENTICATE_WEBAUTHN: '/auth/webauthn/reauthenticate', AUTHENTICATE_WEBAUTHN: '/auth/webauthn/authenticate', LOGIN_WEBAUTHN: '/auth/webauthn/login', SIGNUP_WEBAUTHN: '/auth/webauthn/signup', WEBAUTHN_AUTHENTICATOR: '/account/authenticators/webauthn' }) export const AuthenticatorType = Object.freeze({ TOTP: 'totp', RECOVERY_CODES: 'recovery_codes', WEBAUTHN: 'webauthn' }) function postForm (action, data) { const f = document.createElement('form') f.method = 'POST' f.action = settings.baseUrl + action for (const key in data) { const d = document.createElement('input') d.type = 'hidden' d.name = key d.value = data[key] f.appendChild(d) } document.body.appendChild(f) f.submit() } const tokenStorage = window.sessionStorage export function getSessionToken () { return tokenStorage.getItem('sessionToken') } async function request (method, path, data, headers) { const options = { method, headers: { ...ACCEPT_JSON, ...headers } } if (settings.withCredentials) { options.credentials = 'include' } // Don't pass along authentication related headers to the config endpoint. if (path !== URLs.CONFIG) { if (settings.client === Client.BROWSER) { options.headers['X-CSRFToken'] = getCSRFToken() } else if (settings.client === Client.APP) { // IMPORTANT!: Do NOT use `Client.APP` in a browser context, as you will // be vulnerable to CSRF attacks. This logic is only here for // development/demonstration/testing purposes... options.headers['User-Agent'] = 'django-allauth example app' const sessionToken = getSessionToken() if (sessionToken) { options.headers['X-Session-Token'] = sessionToken } } } if (typeof data !== 'undefined') { options.body = JSON.stringify(data) options.headers['Content-Type'] = 'application/json' } const resp = await fetch(settings.baseUrl + path, options) const msg = await resp.json() if (msg.status === 410) { tokenStorage.removeItem('sessionToken') } if (msg.meta?.session_token) { tokenStorage.setItem('sessionToken', msg.meta.session_token) } if ([401, 410].includes(msg.status) || (msg.status === 200 && msg.meta?.is_authenticated)) { const event = new CustomEvent('allauth.auth.change', { detail: msg }) document.dispatchEvent(event) } return msg } export async function login (data) { return await request('POST', URLs.LOGIN, data) } export async function reauthenticate (data) { return await request('POST', URLs.REAUTHENTICATE, data) } export async function logout () { return await request('DELETE', URLs.SESSION) } export async function signUp (data) { return await request('POST', URLs.SIGNUP, data) } export async function signUpByPasskey (data) { return await request('POST', URLs.SIGNUP_WEBAUTHN, data) } export async function providerSignup (data) { return await request('POST', URLs.PROVIDER_SIGNUP, data) } export async function getProviderAccounts () { return await request('GET', URLs.PROVIDERS) } export async function disconnectProviderAccount (providerId, accountUid) { return await request('DELETE', URLs.PROVIDERS, { provider: providerId, account: accountUid }) } export async function requestPasswordReset (email) { return await request('POST', URLs.REQUEST_PASSWORD_RESET, { email }) } export async function requestLoginCode (email) { return await request('POST', URLs.REQUEST_LOGIN_CODE, { email }) } export async function confirmLoginCode (code) { return await request('POST', URLs.CONFIRM_LOGIN_CODE, { code }) } export async function getEmailVerification (key) { return await request('GET', URLs.VERIFY_EMAIL, undefined, { 'X-Email-Verification-Key': key }) } export async function getEmailAddresses () { return await request('GET', URLs.EMAIL) } export async function getSessions () { return await request('GET', URLs.SESSIONS) } export async function endSessions (ids) { return await request('DELETE', URLs.SESSIONS, { sessions: ids }) } export async function getAuthenticators () { return await request('GET', URLs.AUTHENTICATORS) } export async function getTOTPAuthenticator () { return await request('GET', URLs.TOTP_AUTHENTICATOR) } export async function mfaAuthenticate (code) { return await request('POST', URLs.MFA_AUTHENTICATE, { code }) } export async function mfaReauthenticate (code) { return await request('POST', URLs.MFA_REAUTHENTICATE, { code }) } export async function mfaTrust (trust) { return await request('POST', URLs.MFA_TRUST, { trust }) } export async function activateTOTPAuthenticator (code) { return await request('POST', URLs.TOTP_AUTHENTICATOR, { code }) } export async function deactivateTOTPAuthenticator () { return await request('DELETE', URLs.TOTP_AUTHENTICATOR) } export async function getRecoveryCodes () { return await request('GET', URLs.RECOVERY_CODES) } export async function generateRecoveryCodes () { return await request('POST', URLs.RECOVERY_CODES) } export async function getConfig () { return await request('GET', URLs.CONFIG) } export async function addEmail (email) { return await request('POST', URLs.EMAIL, { email }) } export async function deleteEmail (email) { return await request('DELETE', URLs.EMAIL, { email }) } export async function markEmailAsPrimary (email) { return await request('PATCH', URLs.EMAIL, { email, primary: true }) } export async function requestEmailVerification (email) { return await request('PUT', URLs.EMAIL, { email }) } export async function verifyEmail (key) { return await request('POST', URLs.VERIFY_EMAIL, { key }) } export async function getPasswordReset (key) { return await request('GET', URLs.RESET_PASSWORD, undefined, { 'X-Password-Reset-Key': key }) } export async function resetPassword (data) { return await request('POST', URLs.RESET_PASSWORD, data) } export async function changePassword (data) { return await request('POST', URLs.CHANGE_PASSWORD, data) } export async function getAuth () { return await request('GET', URLs.SESSION) } export async function authenticateByToken (providerId, token, process = AuthProcess.LOGIN) { return await request('POST', URLs.PROVIDER_TOKEN, { provider: providerId, token, process } ) } export function redirectToProvider (providerId, callbackURL, process = AuthProcess.LOGIN) { postForm(URLs.REDIRECT_TO_PROVIDER, { provider: providerId, process, callback_url: window.location.protocol + '//' + window.location.host + callbackURL, csrfmiddlewaretoken: getCSRFToken() }) } export async function getWebAuthnCreateOptions (passwordless) { let url = URLs.WEBAUTHN_AUTHENTICATOR if (passwordless) { url += '?passwordless' } return await request('GET', url) } export async function getWebAuthnCreateOptionsAtSignup () { return await request('GET', URLs.SIGNUP_WEBAUTHN) } export async function addWebAuthnCredential (name, credential) { return await request('POST', URLs.WEBAUTHN_AUTHENTICATOR, { name, credential }) } export async function signupWebAuthnCredential (name, credential) { return await request('PUT', URLs.SIGNUP_WEBAUTHN, { name, credential }) } export async function deleteWebAuthnCredential (ids) { return await request('DELETE', URLs.WEBAUTHN_AUTHENTICATOR, { authenticators: ids }) } export async function updateWebAuthnCredential (id, data) { return await request('PUT', URLs.WEBAUTHN_AUTHENTICATOR, { id, ...data }) } export async function getWebAuthnRequestOptionsForReauthentication () { return await request('GET', URLs.REAUTHENTICATE_WEBAUTHN) } export async function reauthenticateUsingWebAuthn (credential) { return await request('POST', URLs.REAUTHENTICATE_WEBAUTHN, { credential }) } export async function authenticateUsingWebAuthn (credential) { return await request('POST', URLs.AUTHENTICATE_WEBAUTHN, { credential }) } export async function loginUsingWebAuthn (credential) { return await request('POST', URLs.LOGIN_WEBAUTHN, { credential }) } export async function getWebAuthnRequestOptionsForLogin () { return await request('GET', URLs.LOGIN_WEBAUTHN) } export async function getWebAuthnRequestOptionsForAuthentication () { return await request('GET', URLs.AUTHENTICATE_WEBAUTHN) } export function setup (client, baseUrl, withCredentials) { settings.client = client settings.baseUrl = baseUrl settings.withCredentials = withCredentials } ================================================ FILE: examples/react-spa/frontend/src/lib/django.js ================================================ function getCookie (name) { let cookieValue = null if (document.cookie && document.cookie !== '') { const cookies = document.cookie.split(';') for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim() // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)) break } } } return cookieValue } export function getCSRFToken () { return getCookie('csrftoken') } ================================================ FILE: examples/react-spa/frontend/src/mfa/ActivateTOTP.js ================================================ import { useState } from 'react' import * as allauth from '../lib/allauth' import { Navigate, useLoaderData } from 'react-router-dom' import FormErrors from '../components/FormErrors' import Button from '../components/Button' export async function loader ({ params }) { const resp = await allauth.getTOTPAuthenticator() return { totp: resp } } export default function ActivateTOTP (props) { const { totp } = useLoaderData() const [code, setCode] = useState('') const [response, setResponse] = useState({ fetching: false, content: null }) function submit () { setResponse({ ...response, fetching: true }) allauth.activateTOTPAuthenticator(code).then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } if (totp.status === 200 || response.content?.status === 200) { return } return (

    Activate TOTP

    ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/AddWebAuthn.js ================================================ import { useState } from 'react' import { Navigate } from 'react-router-dom' import FormErrors from '../components/FormErrors' import Button from '../components/Button' import * as allauth from '../lib/allauth' import { create, parseCreationOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill' export default function AddWebAuthn (props) { const [passwordless, setPasswordless] = useState(false) const [name, setName] = useState('') const [response, setResponse] = useState({ fetching: false, content: null }) async function submit () { setResponse({ ...response, fetching: true }) try { const optResp = await allauth.getWebAuthnCreateOptions(passwordless) if (optResp.status === 200) { const jsonOptions = optResp.data.creation_options const options = parseCreationOptionsFromJSON(jsonOptions) const credential = await create(options) const addResp = await allauth.addWebAuthnCredential(name, credential) setResponse((r) => { return { ...r, content: addResp } }) } else { setResponse((r) => { return { ...r, content: optResp } }) } } catch (e) { console.error(e) window.alert(e) } setResponse((r) => { return { ...r, fetching: false } }) } if (response.content?.status === 200) { return } return (

    Add Security Key

    ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/AuthenticateCode.js ================================================ import { useState } from 'react' import FormErrors from '../components/FormErrors' import * as allauth from '../lib/allauth' import Button from '../components/Button' import { useAuthInfo } from '../auth/hooks' import { Navigate } from 'react-router-dom' import AuthenticateFlow from './AuthenticateFlow' export default function AuthenticateCode (props) { const [code, setCode] = useState('') const [response, setResponse] = useState({ fetching: false, content: null }) const authInfo = useAuthInfo() if (authInfo?.pendingFlow?.id !== allauth.Flows.MFA_AUTHENTICATE) { return } function submit () { setResponse({ ...response, fetching: true }) allauth.mfaAuthenticate(code).then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } return ( {props.children} ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/AuthenticateFlow.js ================================================ import { Link, Navigate } from 'react-router-dom' import { pathForFlow } from '../auth' import { Flows, AuthenticatorType } from '../lib/allauth' import { useAuthInfo } from '../auth/hooks' const labels = {} labels[AuthenticatorType.TOTP] = 'Use your authenticator app' labels[AuthenticatorType.RECOVERY_CODES] = 'Use a recovery code' labels[AuthenticatorType.WEBAUTHN] = 'Use security key' export default function AuthenticateFlow (props) { const authInfo = useAuthInfo() if (authInfo?.pendingFlow?.id !== Flows.MFA_AUTHENTICATE) { return } const flow = authInfo.pendingFlow return (

    Two-Factor Authentication

    Your account is protected by two-factor authentication.

    {props.children} {flow.types.length > 1 ? <>

    Alternative Options

      {flow.types.map(typ => { return (
    • {labels[typ]}
    • ) })}
    : null}
    ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/AuthenticateRecoveryCodes.js ================================================ import AuthenticateCode from './AuthenticateCode' import { AuthenticatorType } from '../lib/allauth' export default function AuthenticateRecoveryCodes (props) { return (

    Please enter a recovery code:

    ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/AuthenticateTOTP.js ================================================ import AuthenticateCode from './AuthenticateCode' import { AuthenticatorType } from '../lib/allauth' export default function AuthenticateRecoveryCodes (props) { return (

    Please enter an authenticator code:

    ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/AuthenticateWebAuthn.js ================================================ import { useState } from 'react' import { AuthenticatorType, getWebAuthnRequestOptionsForAuthentication, authenticateUsingWebAuthn } from '../lib/allauth' import Button from '../components/Button' import { parseRequestOptionsFromJSON, get } from '@github/webauthn-json/browser-ponyfill' import AuthenticateFlow from './AuthenticateFlow' export default function AuthenticateWebAuthn (props) { const [response, setResponse] = useState({ fetching: false, content: null }) async function submit () { setResponse({ ...response, fetching: true }) try { const optResp = await getWebAuthnRequestOptionsForAuthentication() const jsonOptions = optResp.data.request_options const options = parseRequestOptionsFromJSON(jsonOptions) const credential = await get(options) const reauthResp = await authenticateUsingWebAuthn(credential) setResponse((r) => { return { ...r, content: reauthResp } }) } catch (e) { console.error(e) window.alert(e) } setResponse((r) => { return { ...r, fetching: false } }) } return ( ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/CreateSignupPasskey.js ================================================ import { useState } from 'react' import FormErrors from '../components/FormErrors' import { Flows } from '../lib/allauth' import { Navigate } from 'react-router-dom' import Button from '../components/Button' import { useAuthStatus } from '../auth' import * as allauth from '../lib/allauth' import { create, parseCreationOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill' export default function CreateSignupPasskey () { const [, authInfo] = useAuthStatus() const [name, setName] = useState('') const [response, setResponse] = useState({ fetching: false, content: null }) async function submit () { setResponse({ ...response, fetching: true }) try { const optResp = await allauth.getWebAuthnCreateOptionsAtSignup(true) if (optResp.status === 200) { const jsonOptions = optResp.data.creation_options const options = parseCreationOptionsFromJSON(jsonOptions) const credential = await create(options) const signupResp = await allauth.signupWebAuthnCredential(name, credential) setResponse((r) => { return { ...r, content: signupResp } }) } else { setResponse((r) => { return { ...r, content: optResp } }) } } catch (e) { console.error(e) window.alert(e) } setResponse((r) => { return { ...r, fetching: false } }) } if (response.content?.status === 409 || authInfo.pendingFlow?.id !== Flows.MFA_WEBAUTHN_SIGNUP) { return } return (

    Create Passkey

    You are about to create a passkey for your account. As you can add additional keys later on, you can use a descriptive name to tell the keys apart.

    ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/DeactivateTOTP.js ================================================ import { useState } from 'react' import * as allauth from '../lib/allauth' import { Navigate } from 'react-router-dom' import Button from '../components/Button' export default function DeactivateTOTP (props) { const [response, setResponse] = useState({ fetching: false, content: null }) function submit () { setResponse({ ...response, fetching: true }) allauth.deactivateTOTPAuthenticator().then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } if (response.content?.status === 200) { return } return (

    Deactivate Authenticator App

    You are about to deactivate authenticator app based authentication. Are you sure?

    ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/GenerateRecoveryCodes.js ================================================ import { useState } from 'react' import { Navigate, useLoaderData } from 'react-router-dom' import FormErrors from '../components/FormErrors' import Button from '../components/Button' import * as allauth from '../lib/allauth' export async function loader ({ params }) { const resp = await allauth.getRecoveryCodes() return { recoveryCodes: resp } } export default function GenerateRecoveryCodes () { const { recoveryCodes } = useLoaderData() const [response, setResponse] = useState({ fetching: false, content: null }) function submit () { setResponse({ ...response, fetching: true }) allauth.generateRecoveryCodes().then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } if (response.content?.status === 200) { return } const hasCodes = recoveryCodes.status === 200 && recoveryCodes.data.unused_code_count > 0 return (

    Recovery Codes

    You are about to generate a new set of recovery codes for your account. {hasCodes ? 'This action will invalidate your existing codes.' : ''} Are you sure?

    ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/ListWebAuthn.js ================================================ import { useState } from 'react' import { Link, useLoaderData, Navigate } from 'react-router-dom' import Button from '../components/Button' import * as allauth from '../lib/allauth' export async function loader ({ params }) { const resp = await allauth.getAuthenticators() return { authenticators: resp.data } } function Authenticator (props) { const [name, setName] = useState(props.authenticator.name) const { authenticator } = props function onSubmit (e) { e.preventDefault() props.onSave(name) } return ( {props.editting ?
    setName(e.target.value)} value={name} type='text' />
    : {authenticator.name} } {typeof authenticator.is_passwordless === 'undefined' ? 'Type unspecified' : (authenticator.is_passwordless ? 'Passkey' : 'Security key')} {new Date(authenticator.created_at * 1000).toLocaleString()} {authenticator.last_used_at ? new Date(authenticator.last_used_at * 1000).toLocaleString() : 'Unused'} ) } export default function ListWebAuthn (props) { const { authenticators } = useLoaderData() const [editId, setEditId] = useState(null) const [keys, setKeys] = useState(() => authenticators.filter(authenticator => authenticator.type === allauth.AuthenticatorType.WEBAUTHN)) const [response, setResponse] = useState({ fetching: false, content: null }) async function optimisticSetKeys (newKeys, op) { setResponse({ ...response, fetching: true }) const oldKeys = keys setEditId(null) setKeys(newKeys) try { const ok = await op() if (!ok) { setKeys(oldKeys) } } catch (e) { setKeys(oldKeys) console.error(e) window.alert(e) } setResponse((r) => { return { ...r, fetching: false } }) } async function deleteKey (key) { const newKeys = keys.filter((k) => k.id !== key.id) await optimisticSetKeys(newKeys, async () => { const resp = await allauth.deleteWebAuthnCredential([key.id]) return (resp.status === 200) }) } async function onSave (key, name) { const newKeys = keys.filter((k) => k.id !== key.id) newKeys.push({ ...key, name }) await optimisticSetKeys(newKeys, async () => { const resp = await allauth.updateWebAuthnCredential(key.id, { name }) return (resp.status === 200) }) } if (!keys.length && !response.fetching) { return } return (

    Security Keys

    {keys.map(key => { return ( setEditId(null)} onSave={(name) => onSave(key, name)} onEdit={() => setEditId(key.id)} onDelete={() => deleteKey(key)} /> ) })}
    Name Type Created At Last Used At Actions
    Add
    ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/MFAOverview.js ================================================ import { Link, useLoaderData } from 'react-router-dom' import * as allauth from '../lib/allauth' export async function loader ({ params }) { const resp = await allauth.getAuthenticators() return { authenticators: resp.data } } export default function MFAOverview (props) { const { authenticators } = useLoaderData() const totp = authenticators.find(authenticator => authenticator.type === allauth.AuthenticatorType.TOTP) const webauthn = authenticators.filter(authenticator => authenticator.type === allauth.AuthenticatorType.WEBAUTHN) const recoveryCodes = authenticators.find(authenticator => authenticator.type === allauth.AuthenticatorType.RECOVERY_CODES) return (

    Two-Factor Authentication

    Authenticator App

    {totp ? <>

    Authentication using an authenticator app is active.

    Deactivate : <>

    An authenticator app is not active..

    Activate }

    Security Keys

    {webauthn.length ? <>

    You have added {webauthn.length} security keys.

    Manage : <>

    No security keys have been added.

    Add }

    Recovery Codes

    {!recoveryCodes ? <>

    No recovery codes set up.

    Generate : <>

    There are {recoveryCodes.unused_code_count} out of {recoveryCodes.total_code_count} recovery codes available.

    View Regenerate }
    ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/ReauthenticateCode.js ================================================ import { useState } from 'react' import FormErrors from '../components/FormErrors' import { mfaReauthenticate } from '../lib/allauth' import ReauthenticateFlow from '../account/ReauthenticateFlow' import Button from '../components/Button' export default function ReauthenticateCode (props) { const [code, setCode] = useState('') const [response, setResponse] = useState({ fetching: false, content: null }) function submit () { setResponse({ ...response, fetching: true }) mfaReauthenticate(code).then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } return ( {props.children}
    ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/ReauthenticateRecoveryCodes.js ================================================ import ReauthenticateCode from './ReauthenticateCode' export default function ReauthenticateRecoveryCodes (props) { return (

    Please enter a recovery code:

    ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/ReauthenticateTOTP.js ================================================ import ReauthenticateCode from './ReauthenticateCode' export default function ReauthenticateTOTP (props) { return (

    Please enter an authenticator code:

    ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/ReauthenticateWebAuthn.js ================================================ import { useState } from 'react' import { Flows, getWebAuthnRequestOptionsForReauthentication, reauthenticateUsingWebAuthn } from '../lib/allauth' import ReauthenticateFlow from '../account/ReauthenticateFlow' import Button from '../components/Button' import { parseRequestOptionsFromJSON, get } from '@github/webauthn-json/browser-ponyfill' export default function ReauthenticateWebAuthn () { const [response, setResponse] = useState({ fetching: false, content: null }) async function submit () { setResponse({ ...response, fetching: true }) try { const optResp = await getWebAuthnRequestOptionsForReauthentication() const jsonOptions = optResp.data.request_options const options = parseRequestOptionsFromJSON(jsonOptions) const credential = await get(options) const reauthResp = await reauthenticateUsingWebAuthn(credential) setResponse((r) => { return { ...r, content: reauthResp } }) } catch (e) { console.error(e) window.alert(e) } setResponse((r) => { return { ...r, fetching: false } }) } return ( ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/RecoveryCodes.js ================================================ import { useLoaderData } from 'react-router-dom' import * as allauth from '../lib/allauth' export async function loader ({ params }) { const resp = await allauth.getRecoveryCodes() return { recoveryCodes: resp } } export default function RecoveryCodes (props) { const { recoveryCodes } = useLoaderData() return (

    Recovery Codes

    {recoveryCodes.status === 200 ? <>

    There are {recoveryCodes.data.unused_code_count} out of {recoveryCodes.data.total_code_count} recovery codes available.

    {recoveryCodes.data.unused_codes.join('\n')}
    : <>

    No recovery codes set up.

    }
    ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/SignupByPasskey.js ================================================ import { useState } from 'react' import FormErrors from '../components/FormErrors' import { signUpByPasskey } from '../lib/allauth' import { Link } from 'react-router-dom' import Button from '../components/Button' export default function Signup () { const [email, setEmail] = useState('') const [response, setResponse] = useState({ fetching: false, content: null }) function submit () { setResponse({ ...response, fetching: true }) signUpByPasskey({ email }).then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } return (

    Passkey Sign Up

    Already have an account? Login here.

    Sign up using a password
    ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/Trust.js ================================================ import { useState } from 'react' import AuthenticateCode from './AuthenticateCode' import { AuthenticatorType } from '../lib/allauth' import { useAuthInfo } from '../auth/hooks' import * as allauth from '../lib/allauth' import { Navigate } from 'react-router-dom' import Button from'../components/Button' export default function Trust (props) { const [response, setResponse] = useState({ fetching: false, content: null }) const authInfo = useAuthInfo() if (authInfo?.pendingFlow?.id !== allauth.Flows.MFA_TRUST) { return } function submit (trust) { setResponse({ ...response, fetching: true }) allauth.mfaTrust(trust).then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } return (

    Trust this Browser?

    If you choose to trust this browser, you will not be asked for a verification code the next time you sign in.

    ) } ================================================ FILE: examples/react-spa/frontend/src/mfa/WebAuthnLoginButton.js ================================================ import Button from '../components/Button' import { useState } from 'react' import { getWebAuthnRequestOptionsForLogin, loginUsingWebAuthn } from '../lib/allauth' import { parseRequestOptionsFromJSON, get } from '@github/webauthn-json/browser-ponyfill' export default function WebAuthnLoginButton (props) { const [response, setResponse] = useState({ fetching: false, content: null }) async function submit (e) { e.preventDefault() setResponse({ ...response, fetching: true }) try { const optResp = await getWebAuthnRequestOptionsForLogin() const jsonOptions = optResp.data.request_options const options = parseRequestOptionsFromJSON(jsonOptions) const credential = await get(options) const loginResp = await loginUsingWebAuthn(credential) setResponse((r) => { return { ...r, content: loginResp } }) } catch (e) { console.error(e) window.alert(e) } setResponse((r) => { return { ...r, fetching: false } }) } return } ================================================ FILE: examples/react-spa/frontend/src/socialaccount/GoogleOneTap.js ================================================ import { useState } from 'react' import { useConfig } from '../auth' import { authenticateByToken } from '../lib/allauth' function installGoogleOneTap (cb) { const id = 'google-one-tap' const scr = document.getElementById(id) if (!scr) { const scr = document.createElement('script') scr.id = id scr.src = '//accounts.google.com/gsi/client' scr.async = true scr.addEventListener('load', function () { cb() }) document.body.appendChild(scr) } else { cb() } } export default function GoogleOneTap (props) { const config = useConfig() const [enabled, setEnabled] = useState(() => window.sessionStorage.getItem('googleOneTapEnabled') === 'yes') function onGoogleOneTapInstalled () { const provider = config.data.socialaccount.providers.find(p => p.id === 'google') if (provider && window.google) { function handleCredentialResponse (token) { authenticateByToken(provider.id, { id_token: token.credential, client_id: provider.client_id }, props.process) } window.google.accounts.id.initialize({ client_id: provider.client_id, callback: handleCredentialResponse }) window.google.accounts.id.prompt() } } if (enabled) { installGoogleOneTap(onGoogleOneTapInstalled) return null } function enable () { window.sessionStorage.setItem('googleOneTapEnabled', 'yes') installGoogleOneTap(onGoogleOneTapInstalled) setEnabled(true) } return (
    Mind your privacy: Do you want to demo Google One Tap sign-in?
    ) } ================================================ FILE: examples/react-spa/frontend/src/socialaccount/ManageProviders.js ================================================ import { useState, useEffect } from 'react' import * as allauth from '../lib/allauth' import ProviderList from './ProviderList' import FormErrors from '../components/FormErrors' import Button from '../components/Button' export default function ManageProviders () { const [accounts, setAccounts] = useState([]) const [response, setResponse] = useState({ fetching: false, content: { status: 200, data: [] } }) useEffect(() => { setResponse((r) => { return { ...r, fetching: true } }) allauth.getProviderAccounts().then((resp) => { if (resp.status === 200) { setAccounts(resp.data) } }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) }, []) function disconnect (account) { setResponse({ ...response, fetching: true }) allauth.disconnectProviderAccount(account.provider.id, account.uid).then((resp) => { setResponse((r) => { return { ...r, content: resp } }) if (resp.status === 200) { setAccounts(resp.data) } }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } return (

    Providers

    {accounts.map(account => { return ( ) })}
    UID Account Provider Actions
    {account.uid} {account.display} {account.provider.name}

    Connect

    ) } ================================================ FILE: examples/react-spa/frontend/src/socialaccount/ProviderCallback.js ================================================ import { Navigate, useLocation, Link } from 'react-router-dom' import { URLs, pathForPendingFlow, useAuthStatus } from '../auth' export default function ProviderCallback () { const location = useLocation() const params = new URLSearchParams(location.search) const error = params.get('error') const [auth, status] = useAuthStatus() let url = URLs.LOGIN_URL if (status.isAuthenticated) { url = URLs.LOGIN_REDIRECT_URL } else { url = pathForPendingFlow(auth) || url } if (!error) { return } return ( <>

    Third-Party Login Failure

    Something went wrong.

    Continue ) } ================================================ FILE: examples/react-spa/frontend/src/socialaccount/ProviderList.js ================================================ import { useConfig } from '../auth' import { redirectToProvider, Client, settings } from '../lib/allauth' import Button from '../components/Button' import GoogleOneTap from './GoogleOneTap' export default function ProviderList (props) { const config = useConfig() const providers = config.data.socialaccount.providers if (!providers.length) { return null } return ( <> {settings.client === Client.BROWSER &&
      {providers.map(provider => { return (
    • ) })}
    } ) } ================================================ FILE: examples/react-spa/frontend/src/socialaccount/ProviderSignup.js ================================================ import { useState } from 'react' import FormErrors from '../components/FormErrors' import { providerSignup } from '../lib/allauth' import { Link } from 'react-router-dom' import Button from '../components/Button' export default function ProviderSignup () { const [email, setEmail] = useState('') const [response, setResponse] = useState({ fetching: false, content: null }) function submit () { setResponse({ ...response, fetching: true }) providerSignup({ email }).then((content) => { setResponse((r) => { return { ...r, content } }) }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } return (

    Sign Up

    Already have an account? Login here.

    ) } ================================================ FILE: examples/react-spa/frontend/src/usersessions/Sessions.js ================================================ import { useState, useEffect } from 'react' import { useConfig } from '../auth' import * as allauth from '../lib/allauth' import Button from '../components/Button' export default function Sessions () { const config = useConfig() const [sessions, setSessions] = useState([]) const [response, setResponse] = useState({ fetching: false, content: { status: 200, data: [] } }) useEffect(() => { setResponse((r) => { return { ...r, fetching: true } }) allauth.getSessions().then((resp) => { if (resp.status === 200) { setSessions(resp.data) } }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) }, []) const otherSessions = sessions.filter(session => !session.is_current) function logout (sessions) { setResponse({ ...response, fetching: true }) allauth.endSessions(sessions.map(s => s.id)).then((resp) => { setResponse((r) => { return { ...r, content: resp } }) if (resp.status === 200) { setSessions(resp.data) } }).catch((e) => { console.error(e) window.alert(e) }).then(() => { setResponse((r) => { return { ...r, fetching: false } }) }) } return (

    Sessions

    {config.data.usersessions.track_activity ? : null} {sessions.map((session, i) => { return ( {config.data.usersessions.track_activity ? : null} ) })}
    Started At IP Address BrowserLast Seen AtCurrent Actions
    {new Date(session.created_at * 1000).toLocaleString()} {session.ip} {session.user_agent}{session.last_seen_at}{session.is_current ? '⭐' : ''}
    ) } ================================================ FILE: examples/react-spa/traefik.toml ================================================ defaultEntryPoints = ["http"] [log] level = "DEBUG" [api] dashboard = true [accessLog] [providers] providersThrottleDuration = 10 [providers.file] filename = "traefik.toml" directory = "/etc/traefik/" watch = true [entryPoints] [entryPoints.web] address = ":10000" [http.routers] [http.routers.django] service = "django" rule = "PathPrefix(`/accounts`) || PathPrefix(`/_allauth`) || PathPrefix(`/ninja`) || PathPrefix(`/drf`)" entrypoints = ["web"] [http.routers.react] service = "react" rule = "PathPrefix(`/`)" entrypoints = ["web"] [http.services] [http.services.react.loadBalancer] [[http.services.react.loadBalancer.servers]] url = "http://frontend:3000" [http.services.django.loadBalancer] [[http.services.django.loadBalancer.servers]] url = "http://backend:8000" ================================================ FILE: examples/regular-django/.dockerignore ================================================ .git .cache tmp/ bak/ **/__pycache__ *~ .pytest_cache .vagrant build/ .pytest_cache/ ================================================ FILE: examples/regular-django/Dockerfile ================================================ FROM python:3.12 ENV PYTHONUNBUFFERED 1 # Needed for SAML support RUN apt-get update RUN apt-get -y install libxml2-dev libxmlsec1-dev libxmlsec1-openssl WORKDIR /code/ COPY requirements.txt \ manage.py \ Makefile \ ./ RUN pip install -r requirements.txt COPY ./example ./example/ EXPOSE 8000 ================================================ FILE: examples/regular-django/Makefile ================================================ ifeq ($(wildcard /code),) PYTHON=PYTHONPATH=$$PYTHONPATH:../.. python3 else PYTHON=python3 endif MANAGE_PY=$(PYTHON) manage.py .PHONY: migrate: $(MANAGE_PY) migrate .PHONY: runserver: $(MANAGE_PY) runserver 0.0.0.0:8000 ================================================ FILE: examples/regular-django/README.org ================================================ * Regular Django Example Application ** Run using Docker #+begin_src sh docker-compose up #+end_src You should then be able to open your browser on http://localhost:8000 and see a page with links to sign in or sign up. ** Run Locally Assuming you use virtualenv, follow these steps to download and run the django-allauth example application in this directory: #+begin_src sh git clone git@codeberg.org:allauth/django-allauth.git cd django-allauth/examples/regular-django virtualenv venv . venv/bin/activate pip install "../..[mfa,saml,socialaccount]" #+end_src Now we need to create the database tables and an admin user. Run the following and when prompted to create a superuser choose yes and follow the instructions: #+begin_src sh python manage.py migrate python manage.py createsuperuser #+end_src Finally, run the Django development server: #+begin_src sh python manage.py runserver #+end_src You should then be able to open your browser on http://localhost:8000 and see a page with links to sign in or sign up. ================================================ FILE: examples/regular-django/db/.gitignore ================================================ example.db ================================================ FILE: examples/regular-django/docker-compose.yml ================================================ version: "3" services: web: build: context: ./ dockerfile: Dockerfile volumes: - ./example:/code/example/ - ../../allauth:/code/allauth - ./db:/srv/db environment: DATABASE_NAME: /srv/db/example.db ports: - 8000:8000 command: - make - migrate - runserver ================================================ FILE: examples/regular-django/example/__init__.py ================================================ ================================================ FILE: examples/regular-django/example/asgi.py ================================================ import os from django.core.asgi import get_asgi_application os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings") application = get_asgi_application() ================================================ FILE: examples/regular-django/example/settings.py ================================================ # Django settings for example project. import os from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent DEBUG = True ADMINS = ( # ('Your Name', 'your_email@example.com'), ) MANAGERS = ADMINS EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", # Add 'postgresql_psycopg2', 'postgresql', # 'mysql', 'sqlite3' or 'oracle'. "NAME": os.environ.get( "DATABASE_NAME", os.path.join(BASE_DIR / "db" / "example.db") ), "USER": "", # Not used with sqlite3. "PASSWORD": "", # Not used with sqlite3. "HOST": "", # Set to empty string for localhost. Not used with sqlite3. "PORT": "", # Set to empty string for default. Not used with sqlite3. } } # Local time zone for this installation. Choices can be found here: # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # although not all choices may be available on all operating systems. # On Unix systems, a value of None will cause Django to use the same # timezone as the operating system. # If running in a Windows environment this must be set to the same as your # system time zone. TIME_ZONE = "America/Chicago" # Language code for this installation. All choices can be found here: # http://www.i18nguy.com/unicode/language-identifiers.html LANGUAGE_CODE = "en" LANGUAGES = [ ("ar", "Arabic"), ("az", "Azerbaijani"), ("bg", "Bulgarian"), ("ca", "Catalan"), ("cs", "Czech"), ("da", "Danish"), ("de", "German"), ("el", "Greek"), ("en", "English"), ("es", "Spanish"), ("et", "Estonian"), ("eu", "Basque"), ("fa", "Persian"), ("fi", "Finnish"), ("fr", "French"), ("he", "Hebrew"), ("hr", "Croatian"), ("hu", "Hungarian"), ("id", "Indonesian"), ("it", "Italian"), ("ja", "Japanese"), ("ka", "Georgian"), ("ko", "Korean"), ("ky", "Kyrgyz"), ("lt", "Lithuanian"), ("lv", "Latvian"), ("mn", "Mongolian"), ("nb", "Norwegian Bokmål"), ("nl", "Dutch"), ("pl", "Polish"), ("pt-BR", "Portuguese (Brazil)"), ("pt-PT", "Portuguese (Portugal)"), ("ro", "Romanian"), ("ru", "Russian"), ("sk", "Slovak"), ("sl", "Slovenian"), ("sr", "Serbian"), ("sr-Latn", "Serbian (Latin)"), ("sv", "Swedish"), ("th", "Thai"), ("tr", "Turkish"), ("uk", "Ukrainian"), ("zh-hans", "Chinese (Simplified)"), ("zh-hant", "Chinese (Traditional)"), ] SITE_ID = 1 # If you set this to False, Django will make some optimizations so as not # to load the internationalization machinery. USE_I18N = True # If you set this to False, Django will not format dates, numbers and # calendars according to the current locale USE_L10N = True # Absolute filesystem path to the directory that will hold user-uploaded files. # Example: "/home/media/media.lawrence.com/media/" MEDIA_ROOT = "" # URL that handles the media served from MEDIA_ROOT. Make sure to use a # trailing slash. # Examples: "http://media.lawrence.com/media/", "http://example.com/media/" MEDIA_URL = "" # Absolute path to the directory static files should be collected to. # Don't put anything in this directory yourself; store your static files # in apps' "static/" subdirectories and in STATICFILES_DIRS. # Example: "/home/media/media.lawrence.com/static/" STATIC_ROOT = "" # URL prefix for static files. # Example: "http://media.lawrence.com/static/" STATIC_URL = "/static/" # Additional locations of static files STATICFILES_DIRS = ( # Put strings here, like "/home/html/static" or "C:/www/django/static". # Always use forward slashes, even on Windows. # Don't forget to use absolute paths, not relative paths. ) # List of finder classes that know how to find static files in # various locations. STATICFILES_FINDERS = ( "django.contrib.staticfiles.finders.FileSystemFinder", "django.contrib.staticfiles.finders.AppDirectoriesFinder", # 'django.contrib.staticfiles.finders.DefaultStorageFinder', ) # Make this unique, and don't share it with anybody. SECRET_KEY = "t8_)kj3v!au0!_i56#gre**mkg0&z1df%3bw(#5^#^5e_64!$_" # List of callables that know how to import templates from various sources. TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [ BASE_DIR / "example" / "templates", ], "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.template.context_processors.debug", "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", ], }, }, ] MIDDLEWARE = ( "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.middleware.locale.LocaleMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "allauth.account.middleware.AccountMiddleware", ) AUTHENTICATION_BACKENDS = ("allauth.account.auth_backends.AuthenticationBackend",) ROOT_URLCONF = "example.urls" INSTALLED_APPS = ( "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.humanize", "django.contrib.sites", "django.contrib.messages", "django.contrib.staticfiles", "django.contrib.admin", "allauth", "allauth.account", "allauth.socialaccount", "allauth.mfa", "allauth.socialaccount.providers.dropbox", "allauth.socialaccount.providers.dingtalk", "allauth.socialaccount.providers.facebook", "allauth.socialaccount.providers.edx", "allauth.socialaccount.providers.evernote", "allauth.socialaccount.providers.google", "allauth.socialaccount.providers.github", "allauth.socialaccount.providers.linkedin_oauth2", "allauth.socialaccount.providers.mediawiki", "allauth.socialaccount.providers.openid", "allauth.socialaccount.providers.openid_connect", "allauth.socialaccount.providers.pinterest", "allauth.socialaccount.providers.pocket", "allauth.socialaccount.providers.reddit", "allauth.socialaccount.providers.saml", "allauth.socialaccount.providers.shopify", "allauth.socialaccount.providers.slack", "allauth.socialaccount.providers.snapchat", "allauth.socialaccount.providers.soundcloud", "allauth.socialaccount.providers.stackexchange", "allauth.socialaccount.providers.telegram", "allauth.socialaccount.providers.twitch", "allauth.socialaccount.providers.twitter", "allauth.socialaccount.providers.twitter_oauth2", "allauth.socialaccount.providers.vimeo", "allauth.socialaccount.providers.vimeo_oauth2", "allauth.socialaccount.providers.weibo", "allauth.socialaccount.providers.xing", "allauth.usersessions", "example.users", ) AUTH_USER_MODEL = "users.User" AUTH_PASSWORD_VALIDATORS = [ { "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", "OPTIONS": { "min_length": 9, }, } ] ALLOWED_HOSTS = ["127.0.0.1", "localhost"] SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") ACCOUNT_LOGIN_BY_CODE_ENABLED = True ACCOUNT_EMAIL_VERIFICATION = "mandatory" ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True ACCOUNT_LOGIN_METHODS = { "email", } ACCOUNT_PASSWORD_RESET_BY_CODE_ENABLED = True ACCOUNT_SIGNUP_FIELDS = ["email*", "password1*", "password2*"] ACCOUNT_ADAPTER = "example.users.allauth.AccountAdapter" MFA_SUPPORTED_TYPES = [ "webauthn", "totp", "recovery_codes", ] MFA_PASSKEY_LOGIN_ENABLED = True MFA_PASSKEY_SIGNUP_ENABLED = True try: from .local_settings import * # noqa except ImportError: pass ================================================ FILE: examples/regular-django/example/templates/429.html ================================================ {% extends "allauth/layouts/entrance.html" %} {% load allauth %} {% block head_title %} Too Many Requests {% endblock head_title %} {% block content %} {% element h1 %} Too Many Requests {% endelement %}

    You are sending too many requests.

    {% endblock content %} ================================================ FILE: examples/regular-django/example/templates/account/base_manage_email.html ================================================ {% extends "allauth/layouts/manage.html" %} {% block nav_class_email %}{{ block.super }} active{% endblock %} ================================================ FILE: examples/regular-django/example/templates/account/base_manage_password.html ================================================ {% extends "allauth/layouts/manage.html" %} {% block nav_class_password %}{{ block.super }} active{% endblock %} ================================================ FILE: examples/regular-django/example/templates/account/base_manage_phone.html ================================================ {% extends "allauth/layouts/manage.html" %} {% block nav_class_phone %}{{ block.super }} active{% endblock %} ================================================ FILE: examples/regular-django/example/templates/allauth/elements/alert.html ================================================ {% load allauth %} ================================================ FILE: examples/regular-django/example/templates/allauth/elements/badge.html ================================================ {% load allauth %} {% setvar variant %} {% if "warning" in attrs.tags %} warning {% elif "danger" in attrs.tags %} danger {% elif "secondary" in attrs.tags %} secondary {% elif "success" in attrs.tags %} success {% else %} primary {% endif %} {% endsetvar %} {% slot %} {% endslot %} ================================================ FILE: examples/regular-django/example/templates/allauth/elements/button.html ================================================ {% load allauth %} {% comment %} djlint:off {% endcomment %} <{% if attrs.href %}a href="{{ attrs.href }}"{% else %}button{% endif %} {% if attrs.form %}form="{{ attrs.form }}"{% endif %} {% if attrs.id %}id="{{ attrs.id }}"{% endif %} {% if attrs.name %}name="{{ attrs.name }}"{% endif %} {% if attrs.type %}type="{{ attrs.type }}"{% endif %} {% if attrs.value %}value="{{ attrs.value }}"{% endif %} class="{% block class %} btn {% if "link" in attrs.tags %}btn-link {% else %} {% if "prominent" in attrs.tags %}btn-lg{% elif "minor" in attrs.tags %}btn-sm{% endif %} btn-{% if 'outline' in attrs.tags %}outline-{% endif %}{% if "danger" in attrs.tags %}danger{% elif "secondary" in attrs.tags %}secondary{% elif "warning" in attrs.tags %}warning{% else %}primary{% endif %} {% endif %}{% endblock %}"> {% if "tool" in attrs.tags %} {% if "delete" in attrs.tags %} {% elif "edit" in attrs.tags %} {% endif %} {% endif %} {% if not "tool" in attrs.tags %} {% slot %} {% endslot %} {% endif %} ================================================ FILE: examples/regular-django/example/templates/allauth/elements/button__entrance.html ================================================ {% extends "allauth/elements/button.html" %} {% load allauth %} {% block class %} {{ block.super }} w-100 {% endblock %} ================================================ FILE: examples/regular-django/example/templates/allauth/elements/button_group.html ================================================ {% load allauth %}
    {% slot %} {% endslot %}
    ================================================ FILE: examples/regular-django/example/templates/allauth/elements/field.html ================================================ {% load allauth %} {% if attrs.type == "checkbox" or attrs.type == "radio" %}
    {% if slots.help_text %}
    {% slot help_text %} {% endslot %}
    {% endif %}
    {% elif attrs.type == "textarea" %}
    {% elif attrs.type == "hidden" %} {% elif attrs.type == "select" %}
    {% if not attrs.unlabeled %} {% endif %} {% if attrs.unlabeled %} {% endif %}
    {% else %}
    {% if not attrs.unlabeled %} {% endif %} {% if attrs.unlabeled %} {% endif %} {% if slots.help_text %}
    {% slot help_text %} {% endslot %}
    {% endif %} {% if attrs.errors %} {% for error in attrs.errors %}{% endfor %} {% endif %}
    {% endif %} ================================================ FILE: examples/regular-django/example/templates/allauth/elements/fields.html ================================================ {% load allauth %} {% for bound_field in attrs.form %} {% element field unlabeled=attrs.unlabeled name=bound_field.name type=bound_field.field.widget.input_type required=bound_field.field.required value=bound_field.value id=bound_field.auto_id errors=bound_field.errors placeholder=bound_field.field.widget.attrs.placeholder tabindex=bound_field.field.widget.attrs.tabindex autocomplete=bound_field.field.widget.attrs.autocomplete style=bound_field.field.widget.attrs.style choices=bound_field.field.choices %} {% slot label %} {{ bound_field.label }} {% endslot %} {% if bound_field.field.help_text %} {% slot help_text %} {{ bound_field.field.help_text }} {% endslot %} {% endif %} {% endelement %} {% endfor %} ================================================ FILE: examples/regular-django/example/templates/allauth/elements/form.html ================================================ {% load allauth %} {% for err in attrs.form.non_field_errors %}{% endfor %}
    {% if not attrs.no_visible_fields %}
    {% endif %} {% slot body %} {% endslot %} {% if not attrs.no_visible_fields %}
    {% endif %} {% if not attrs.no_visible_fields %}{% endif %}
    ================================================ FILE: examples/regular-django/example/templates/allauth/elements/form__entrance.html ================================================ {% extends "allauth/elements/form.html" %} {% block form_class %}{% endblock %} ================================================ FILE: examples/regular-django/example/templates/allauth/elements/h1.html ================================================ {% load allauth %}

    {% slot %} {% endslot %}

    ================================================ FILE: examples/regular-django/example/templates/allauth/elements/h1__entrance.html ================================================ {% load allauth %}

    {% slot %} {% endslot %}

    ================================================ FILE: examples/regular-django/example/templates/allauth/elements/h2__entrance.html ================================================ {% load allauth %}

    {% slot %} {% endslot %}

    ================================================ FILE: examples/regular-django/example/templates/allauth/elements/img.html ================================================
    ================================================ FILE: examples/regular-django/example/templates/allauth/elements/panel.html ================================================ {% load allauth %}
    {% slot title %} {% endslot %}
    {% slot body %} {% endslot %}
    {% if slots.actions %} {% endif %}
    ================================================ FILE: examples/regular-django/example/templates/allauth/elements/provider.html ================================================ {% load allauth %}
  • {{ attrs.name }}
  • ================================================ FILE: examples/regular-django/example/templates/allauth/elements/provider_list.html ================================================ {% load allauth %}
      {% slot %} {% endslot %}
    ================================================ FILE: examples/regular-django/example/templates/allauth/elements/table.html ================================================ {% load allauth %} {% slot %} {% endslot %}
    ================================================ FILE: examples/regular-django/example/templates/allauth/layouts/base.html ================================================ {% load i18n %} {% block head_title %}{% endblock %} {% translate "Skip to main content" %}
    {% block body %} {% block content %} {% endblock content %} {% endblock body %} {% block extra_body %} {% endblock extra_body %} ================================================ FILE: examples/regular-django/example/templates/allauth/layouts/entrance.html ================================================ {% extends "allauth/layouts/base.html" %} {% load i18n allauth %} {% block body %}
    {% if messages %} {% for message in messages %} {% element alert level=message.tags %} {% slot message %} {{ message }} {% endslot %} {% endelement %} {% endfor %} {% endif %}
    {% block content %}{% endblock %}
    {% endblock %} ================================================ FILE: examples/regular-django/example/templates/allauth/layouts/manage.html ================================================ {% extends "allauth/layouts/base.html" %} {% load allauth %} {% block body %}
    {% if messages %}
    {% for message in messages %} {% element alert level=message.tags %} {% slot message %} {{ message }} {% endslot %} {% endelement %} {% endfor %}
    {% endif %} {% block content %}{% endblock %}
    {% endblock %} ================================================ FILE: examples/regular-django/example/templates/index.html ================================================ {% extends "allauth/layouts/entrance.html" %} {% load allauth %} {% block head_title %}Example Project{% endblock %} {% block content %} {% element h1 %} Example Project {% endelement %}

    Welcome to the django-allauth example project.

    {% endblock content %} ================================================ FILE: examples/regular-django/example/templates/mfa/base_manage.html ================================================ {% extends "allauth/layouts/manage.html" %} {% block nav_class_mfa %}{{ block.super }} active{% endblock %} ================================================ FILE: examples/regular-django/example/templates/profile.html ================================================ {% extends "allauth/layouts/manage.html" %} {% load allauth %} {% block head_title %}Profile{% endblock %} {% block content %} {% element h1 %} Profile {% endelement %}

    Your profile

    {% endblock content %} ================================================ FILE: examples/regular-django/example/templates/socialaccount/base_manage.html ================================================ {% extends "allauth/layouts/manage.html" %} {% block nav_class_socialaccount %}{{ block.super }} active{% endblock %} ================================================ FILE: examples/regular-django/example/templates/usersessions/base_manage.html ================================================ {% extends "allauth/layouts/manage.html" %} {% block nav_class_usersessions %}{{ block.super }} active{% endblock %} ================================================ FILE: examples/regular-django/example/urls.py ================================================ from django.contrib import admin from django.urls import include, path from django.views.generic.base import TemplateView from allauth.account.decorators import secure_admin_login admin.autodiscover() admin.site.login = secure_admin_login(admin.site.login) urlpatterns = [ path("", TemplateView.as_view(template_name="index.html")), path("accounts/", include("allauth.urls")), path("accounts/profile/", TemplateView.as_view(template_name="profile.html")), path("admin/", admin.site.urls), path("i18n/", include("django.conf.urls.i18n")), path("", include("allauth.idp.urls")), ] ================================================ FILE: examples/regular-django/example/users/__init__.py ================================================ ================================================ FILE: examples/regular-django/example/users/allauth.py ================================================ import typing from django.contrib import messages from example.users.models import User from allauth.account.adapter import DefaultAccountAdapter class AccountAdapter(DefaultAccountAdapter): def set_phone(self, user, phone: str, verified: bool): user.phone = phone user.phone_verified = verified user.save(update_fields=["phone", "phone_verified"]) def get_phone(self, user) -> typing.Optional[typing.Tuple[str, bool]]: if user.phone: return user.phone, user.phone_verified return None def set_phone_verified(self, user, phone): self.set_phone(user, phone, True) def send_verification_code_sms(self, user, phone: str, code: str, **kwargs): messages.add_message( self.request, messages.WARNING, f"⚠️ SMS demo stub: assume code {code} was sent to {phone}.", ) def send_unknown_account_sms(self, phone: str, **kwargs): messages.add_message( self.request, messages.WARNING, f"⚠️ SMS demo stub: Enumeration prevention: texted {phone} informing no account exists.", ) def send_account_already_exists_sms(self, phone: str, **kwargs): messages.add_message( self.request, messages.WARNING, f"⚠️ SMS demo stub: Enumeration prevention: texted {phone} informing account already exists.", ) def get_user_by_phone(self, phone): return User.objects.filter(phone=phone).order_by("-phone_verified").first() ================================================ FILE: examples/regular-django/example/users/apps.py ================================================ from django.apps import AppConfig from django.utils.translation import gettext_lazy as _ class UsersConfig(AppConfig): name = "example.users" verbose_name = _("Users") default_auto_field = "django.db.models.AutoField" ================================================ FILE: examples/regular-django/example/users/migrations/0001_initial.py ================================================ import uuid import django.contrib.auth.models import django.contrib.auth.validators import django.utils.timezone from django.db import migrations, models class Migration(migrations.Migration): initial = True dependencies = [ ("auth", "0012_alter_user_first_name_max_length"), ] operations = [ migrations.CreateModel( name="User", fields=[ ("password", models.CharField(max_length=128, verbose_name="password")), ( "last_login", models.DateTimeField( blank=True, null=True, verbose_name="last login" ), ), ( "is_superuser", models.BooleanField( default=False, help_text="Designates that this user has all permissions without explicitly assigning them.", verbose_name="superuser status", ), ), ( "username", models.CharField( error_messages={ "unique": "A user with that username already exists." }, help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", max_length=150, unique=True, validators=[ django.contrib.auth.validators.UnicodeUsernameValidator() ], verbose_name="username", ), ), ( "first_name", models.CharField( blank=True, max_length=150, verbose_name="first name" ), ), ( "last_name", models.CharField( blank=True, max_length=150, verbose_name="last name" ), ), ( "email", models.EmailField( blank=True, max_length=254, verbose_name="email address" ), ), ( "is_staff", models.BooleanField( default=False, help_text="Designates whether the user can log into this admin site.", verbose_name="staff status", ), ), ( "is_active", models.BooleanField( default=True, help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.", verbose_name="active", ), ), ( "date_joined", models.DateTimeField( default=django.utils.timezone.now, verbose_name="date joined" ), ), ( "id", models.UUIDField( default=uuid.uuid4, primary_key=True, serialize=False ), ), ( "phone", models.CharField(blank=True, max_length=16, null=True, unique=True), ), ("phone_verified", models.BooleanField(default=False)), ( "groups", models.ManyToManyField( blank=True, help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", related_name="user_set", related_query_name="user", to="auth.group", verbose_name="groups", ), ), ( "user_permissions", models.ManyToManyField( blank=True, help_text="Specific permissions for this user.", related_name="user_set", related_query_name="user", to="auth.permission", verbose_name="user permissions", ), ), ], options={ "verbose_name": "user", "verbose_name_plural": "users", "abstract": False, }, managers=[ ("objects", django.contrib.auth.models.UserManager()), ], ), ] ================================================ FILE: examples/regular-django/example/users/migrations/__init__.py ================================================ ================================================ FILE: examples/regular-django/example/users/models.py ================================================ import uuid from django.contrib.auth.models import AbstractUser from django.db import models class User(AbstractUser): id = models.UUIDField(primary_key=True, default=uuid.uuid4) phone = models.CharField(max_length=16, unique=True, blank=True, null=True) phone_verified = models.BooleanField(default=False) ================================================ FILE: examples/regular-django/example/wsgi.py ================================================ import os from django.core.wsgi import get_wsgi_application os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings") application = get_wsgi_application() ================================================ FILE: examples/regular-django/manage.py ================================================ #!/usr/bin/env python import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings") from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) ================================================ FILE: examples/regular-django/requirements.txt ================================================ django-allauth[mfa,saml,socialaccount,steam]>=65.15.0 ================================================ FILE: manage.py ================================================ #!/usr/bin/env python import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.projects.regular.settings") from django.core import management # noqa: E402 if __name__ == "__main__": management.execute_from_command_line() ================================================ FILE: mise.toml ================================================ [tools] python = "3.13" ruff = "latest" [env] _.python.venv = ".venv" [tasks.install] run = ["python -m venv .venv", ".venv/bin/pip install -r requirements-dev.txt"] ================================================ FILE: noxfile.py ================================================ import os import nox @nox.session def docs(session): session.install( "Django==5.2", "Sphinx>=7.4.7,<8", "sphinx_rtd_theme", "djangorestframework>=3.15.2,<4", "django-ninja>=1.3.0,<2", "snowballstemmer<3", # https://github.com/sphinx-doc/sphinx/issues/13533 ) session.run("make", "docs") @nox.session(tags=["lint"]) def lint(session): session.install("bandit") session.run("bandit", "-c", "pyproject.toml", "-r", "allauth/") @nox.session(tags=["lint"]) def isort(session): session.install("isort==5.13.2") session.run("isort", "--check-only", "--diff", "--gitignore", ".") @nox.session(tags=["lint"]) def flake8(session): session.install("flake8==7.2.0") session.run("flake8", "allauth/", "tests/") @nox.session(tags=["lint"]) def black(session): session.install("black==24.4.0") session.run("black", "--check", ".") @nox.session(tags=["lint"]) def djlint(session): session.install("djlint==1.34.1") session.run( "djlint", "--configuration", ".djlintrc", "--check", "./allauth", "./examples" ) DJANGO_PYTHON_REQ = { "4.2.20": ("3.10", "3.11", "3.12"), "5.1": ("3.10", "3.11", "3.12", "3.13"), "5.2": ("3.10", "3.11", "3.12", "3.13", "3.14"), "6.0": ("3.12", "3.13", "3.14"), } DJANGO_LTS = "5.2" @nox.session(python=["3.10", "3.11", "3.12", "3.13", "3.14"]) @nox.parametrize("django", list(DJANGO_PYTHON_REQ.keys())) @nox.parametrize( "project", ["regular", "headless_only", "account_only", "login_required_mw"] ) def test(session, django, project): django_version = tuple(map(int, django.split("."))) if django != DJANGO_LTS: last_django_python = DJANGO_PYTHON_REQ[django][-1] first_django_python = DJANGO_PYTHON_REQ[django][0] if session.python not in (first_django_python, last_django_python): print( f"Skipping: only current LTS is tested against python{session.python}, not Django {django}" ) return if project == "login_required_mw" and django_version < (5, 1): print(f"Skipping: project {project} does not support Django {django}") return if session.python not in DJANGO_PYTHON_REQ[django]: print(f"Skipping: Django {django} does not support python{session.python}") return django_version = f"{django}.0" if django != "6.0" else "6.0rc1" session.install( f"django~={django_version}", "pytest>=8.3.5,<9", "pytest-asyncio==1.3.0", "pytest-django>=4.11,<5", "Pillow>=9.0", "coverage==7.6.1", # SAML is disabled in CI # "python3-saml==1.16.0", # "xmlsec==1.3.14", # "lxml==5.2.1", "pyyaml>=6.0.2,<7", "psycopg2-binary>=2.9.10,<3", "djangorestframework>=3.15.2,<4", "django-ninja>=1.3.0,<2", "mypy==1.19.1", ".[mfa,openid,socialaccount,steam]", # SAML is disabled in CI ) session.run("/bin/sh", "-c", "cd allauth; python ../manage.py compilemessages") run_coveralls = ( os.environ.get("GITHUB_TOKEN") and project == "regular" and django == "5.2" and session.python == "3.14" ) if run_coveralls: session.install("coveralls") session.run( "coverage", "run", "-m", "pytest", f"--ds=tests.projects.{project}.settings", "tests/", ) if run_coveralls: session.run("coveralls", "--service=github") if django == "5.2" and session.python == "3.14": session.install( "django-stubs~=5.2.9", "types-requests==2.32.4.20260107", ) session.run("mypy", "allauth") ================================================ FILE: package.json ================================================ { "name": "django-allauth", "devDependencies": { "standard": "^17.1.2" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } ================================================ FILE: pyproject.toml ================================================ [build-system] requires = ["setuptools>=75.3.0", "setuptools-scm>=8"] build-backend = "setuptools.build_meta" [project] name = "django-allauth" dynamic = ["version"] authors = [ {name = "Raymond Penners", email = "raymond.penners@intenct.nl"}, ] description = "Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication." readme = "README.rst" license = {text = "MIT"} classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries :: Python Modules", "Environment :: Web Environment", "Topic :: Internet", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Framework :: Django", "Framework :: Django :: 4.2", "Framework :: Django :: 5.0", "Framework :: Django :: 5.1", "Framework :: Django :: 5.2", "Framework :: Django :: 6.0", ] requires-python = ">= 3.10" dependencies = [ "Django >= 4.2.16", "asgiref >= 3.8.1", ] [project.optional-dependencies] headless = [ "pyjwt[crypto] >= 2.0,<3", ] headless-spec = [ "PyYAML >= 6,<7", ] idp-oidc = [ "oauthlib >= 3.3.0,<4", "pyjwt[crypto] >= 2.0,<3", ] mfa = [ "qrcode >= 7.0.0,<9", "fido2 >= 1.1.2,<3", ] openid = [ "python3-openid >= 3.0.8,<4", ] saml = [ "python3-saml>=1.15.0,<2.0.0", ] steam = [ "python3-openid >= 3.0.8,<4", ] socialaccount = [ "oauthlib >= 3.3.0,<4", "requests >= 2.0.0,<3", "pyjwt[crypto] >= 2.0,<3", ] [project.urls] Homepage = "https://allauth.org" Documentation = "https://docs.allauth.org/en/latest/" "Release notes" = "https://codeberg.org/allauth/django-allauth/src/branch/main/ChangeLog.rst" Source = "https://codeberg.org/allauth/django-allauth" Tracker = "https://codeberg.org/allauth/django-allauth/issues" Funding = "https://github.com/sponsors/pennersr" [tool.bandit] exclude_dirs = ["tests", "allauth/conftest.py"] exclude = ["test_*"] [tool.isort] profile = "black" known_first_party = ["allauth"] indent = 4 combine_star = true combine_as_imports = true include_trailing_comma = true multi_line_output = 3 lines_after_imports = 2 known_django = ["django"] extra_standard_library = [ "types", "requests" ] sections = ["FUTURE", "STDLIB", "DJANGO", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] [tool.mypy] mypy_path = "." plugins = [ "mypy_django_plugin.main" ] strict_optional = true disable_error_code = [ "import-untyped", "import-not-found", ] warn_unused_configs = true warn_unused_ignores = true exclude = [ "allauth/account/migrations/.*", ] [tool.django-stubs] django_settings_module = "tests.projects.regular.settings" [tool.setuptools.dynamic] version = {attr = "allauth.__version__"} [tool.setuptools_scm] [tool.setuptools.packages.find] include = ["allauth*"] [tool.setuptools.package-data] "*" = ["*.mo"] ================================================ FILE: pytest.ini ================================================ [pytest] DJANGO_SETTINGS_MODULE = tests.projects.regular.settings python_files = tests.py test_*.py *_tests.py ================================================ FILE: requirements-dev.txt ================================================ # Install the package itself with all extras -e .[headless,socialaccount,mfa,idp-oidc,openid,steam,saml] Sphinx sphinx_rtd_theme # Testing tools pytest>=7.4 pytest-asyncio==1.3.0 pytest-django>=4.5.2 pytest-cov # Code quality isort==5.13.2 bandit>=1.8.6,<2 black==24.4.0 djlint==1.34.1 flake8>=7.3.0,<8 mypy==1.19.1 # Type stubs django-stubs==5.2.9 types-requests==2.32.4.20260107 oauthlib>=3.3.0,<4 Pillow>=9.0 psycopg[binary]>=3.2.10 djangorestframework>=3.15.2,<4 django-ninja>=1.3.0,<2 pyyaml>=6.0.2,<7 nox # IDE python-lsp-server==1.13.1 # https://pypi.org/project/python-lsp-server/ pylsp-rope==0.1.17 # https://pypi.org/project/pylsp-rope/ # Release build>=1.3.0,<2 twine>=6.2.0,<7 ================================================ FILE: setup.cfg ================================================ [flake8] max-line-length = 88 # Black ignore = E203, W503, E501, E231 ================================================ FILE: tests/__init__.py ================================================ ================================================ FILE: tests/apps/__init__.py ================================================ ================================================ FILE: tests/apps/account/__init__.py ================================================ ================================================ FILE: tests/apps/account/internal/__init__.py ================================================ ================================================ FILE: tests/apps/account/internal/test_emailkit.py ================================================ import pytest from allauth.account.internal import emailkit @pytest.mark.parametrize( "email,valid", [ ( "this.email.address.is.a.bit.too.long.but.should.still.validate@example.com", True, ), ( ("x" * 300) + "this.email.address.is.a.bit.too.long.but.should.still.validate@example.com", False, ), ], ) def test_email_validation(email, valid): clean_email = emailkit.valid_email_or_none(email) if valid: assert clean_email == email else: assert clean_email is None ================================================ FILE: tests/apps/account/test_adapter.py ================================================ import sys from http import HTTPStatus from django.core.exceptions import PermissionDenied from django.http import HttpResponseRedirect from django.urls import reverse import pytest from allauth.account.adapter import DefaultAccountAdapter from allauth.core.exceptions import ImmediateHttpResponse class PreLoginRedirectAccountAdapter(DefaultAccountAdapter): def pre_login(self, *args, **kwargs): raise ImmediateHttpResponse(HttpResponseRedirect("/foo")) def test_adapter_pre_login(settings, user, user_password, client): settings.ACCOUNT_ADAPTER = ( "tests.apps.account.test_adapter.PreLoginRedirectAccountAdapter" ) resp = client.post( reverse("account_login"), {"login": user.username, "password": user_password}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == "/foo" @pytest.mark.parametrize( "x_forwarded_for,expected_ip", [ ("", None), ("192.168.1.1", "192.168.1.1"), (" 192.168.1.1", "192.168.1.1"), ("192.168.1.1 ", "192.168.1.1"), (" 192.168.1.1:12345 ", "192.168.1.1"), (" [2001:db8::1:0]:12345 ", "2001:db8::1:0"), ("192.168.1.1:12345", "192.168.1.1"), ( "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:db8:85a3::8a2e:370:7334", ), ( "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:12345", "2001:db8:85a3::8a2e:370:7334", ), ("2001:db8::1:0", "2001:db8::1:0"), ("[2001:db8::1:0]:12345", "2001:db8::1:0"), ("::abc:7:def", "::abc:7:def"), ("::1", "::1"), ("[::1]:12345", "::1"), ] + ( [ # not supported by ipaddress py3.8 ("fe80::1234%1", "fe80::1234%1"), ] if sys.version_info >= (3, 9) else [] ), ) def test_get_client_ip_vs_x_forwarded_for(rf, x_forwarded_for, expected_ip, settings): settings.ALLAUTH_TRUSTED_PROXY_COUNT = 1 request = rf.get("/", HTTP_X_FORWARDED_FOR=x_forwarded_for) ip = DefaultAccountAdapter(request=request).get_client_ip(request) assert ip == (expected_ip if expected_ip else request.META["REMOTE_ADDR"]) @pytest.mark.parametrize( "x_forwarded_for", [ " ", "999.999.999.999", "[999.999.999.999]", "999.999.999.999:12345", "[999.999.999.999]:12345", "[192.0.0.2]", "[192.0.0.2]:12345", "192.[0].0.2", "192.[0].0.2:12345", "192.[0.0].2:12345", "[192.[0.0].2:12345", "2001:db8::1:0:12345", "[2001:db8::1:0:12345]", "2001:0db8:85a3:0000:0000:8a2e:0370:12345", "[2001:0db8:85a3:0000:0000:8a2e:0370]:7334:12345", "XwDxNk4JQhUft')) OR 18=(SELECT 18 FROM PG_SLEEP(15))--", ], ) def test_get_client_ip_vs_invalid_x_forwarded_for(rf, x_forwarded_for, settings): settings.ALLAUTH_TRUSTED_PROXY_COUNT = 1 request = rf.get("/", HTTP_X_FORWARDED_FOR=x_forwarded_for) with pytest.raises(PermissionDenied): DefaultAccountAdapter(request=request).get_client_ip(request) ================================================ FILE: tests/apps/account/test_ajax.py ================================================ import json from http import HTTPStatus from django.conf import settings from django.urls import reverse import pytest from pytest_django.asserts import assertRedirects from allauth.account import app_settings @pytest.mark.parametrize( "headers,ajax_expected", [ ({}, False), ({"HTTP_X_REQUESTED_WITH": "XMLHttpRequest"}, True), ({"HTTP_ACCEPT": "application/json"}, True), ], ) def test_ajax_headers(db, client, headers, ajax_expected): resp = client.post( reverse("account_signup"), { "username": "johndoe", "email": "john@example.org", "email2": "john@example.org", "password1": "johndoe", "password2": "johndoe", }, **headers, ) if ajax_expected: assert resp.status_code == HTTPStatus.OK assert resp.json()["location"] == settings.LOGIN_REDIRECT_URL assert resp.json()["location"] == settings.LOGIN_REDIRECT_URL else: assert resp.status_code == HTTPStatus.FOUND assertRedirects( resp, settings.LOGIN_REDIRECT_URL, fetch_redirect_response=False ) def test_ajax_password_reset(client, user, mailoutbox): resp = client.post( reverse("account_reset_password"), data={"email": user.email}, HTTP_X_REQUESTED_WITH="XMLHttpRequest", ) assert len(mailoutbox) == 1 assert mailoutbox[0].to == [user.email] assert resp["content-type"] == "application/json" def test_ajax_login_fail(client, db): resp = client.post( reverse("account_login"), {}, HTTP_X_REQUESTED_WITH="XMLHttpRequest", ) assert resp.status_code == HTTPStatus.BAD_REQUEST json.loads(resp.content.decode("utf8")) # TODO: Actually test something def test_ajax_login_success(settings, user, user_password, client): settings.ACCOUNT_EMAIL_VERIFICATION = app_settings.EmailVerificationMethod.OPTIONAL resp = client.post( reverse("account_login"), {"login": user.username, "password": user_password}, HTTP_X_REQUESTED_WITH="XMLHttpRequest", ) assert resp.status_code == HTTPStatus.OK data = json.loads(resp.content.decode("utf8")) assert data["location"] == "/accounts/profile/" ================================================ FILE: tests/apps/account/test_auth_backends.py ================================================ from unittest.mock import patch from django.contrib.auth import get_user_model from django.test import TestCase from django.test.utils import override_settings import pytest from allauth.account import app_settings from allauth.account.auth_backends import AuthenticationBackend class AuthenticationBackendTests(TestCase): def setUp(self): user = get_user_model().objects.create( is_active=True, email="john@example.com", username="john" ) user.set_password(user.username) user.save() self.user = user @override_settings( ACCOUNT_LOGIN_METHODS={app_settings.LoginMethod.USERNAME} ) # noqa def test_auth_by_username(self): user = self.user backend = AuthenticationBackend() self.assertEqual( backend.authenticate( request=None, username=user.username, password=user.username ).pk, user.pk, ) self.assertEqual( backend.authenticate( request=None, username=user.email, password=user.username ), None, ) @override_settings(ACCOUNT_LOGIN_METHODS={app_settings.LoginMethod.EMAIL}) # noqa def test_auth_by_email(self): user = self.user backend = AuthenticationBackend() self.assertEqual( backend.authenticate( request=None, username=user.email, password=user.username ).pk, user.pk, ) self.assertEqual( backend.authenticate( request=None, username=user.username, password=user.username ), None, ) @override_settings( ACCOUNT_LOGIN_METHODS={ app_settings.LoginMethod.EMAIL, app_settings.LoginMethod.USERNAME, } ) # noqa def test_auth_by_username_or_email(self): user = self.user backend = AuthenticationBackend() self.assertEqual( backend.authenticate( request=None, username=user.email, password=user.username ).pk, user.pk, ) self.assertEqual( backend.authenticate( request=None, username=user.username, password=user.username ).pk, user.pk, ) @pytest.mark.parametrize( "login_methods", [ {app_settings.LoginMethod.EMAIL}, {app_settings.LoginMethod.USERNAME}, {app_settings.LoginMethod.USERNAME, app_settings.LoginMethod.EMAIL}, ], ) def test_account_enumeration_timing_attack(user, db, rf, settings, login_methods): settings.ACCOUNT_LOGIN_METHODS = login_methods with patch("django.contrib.auth.models.User.set_password") as set_password_mock: with patch( "django.contrib.auth.models.User.check_password", new=set_password_mock ): backend = AuthenticationBackend() backend.authenticate( rf.get("/"), email="not@known.org", username="not-known", password="secret", ) set_password_mock.assert_called_once() set_password_mock.reset_mock() backend.authenticate(rf.get("/"), username=user.username, password="secret") set_password_mock.assert_called_once() set_password_mock.reset_mock() backend.authenticate( rf.get("/"), email=user.email, username="not-known", password="secret" ) set_password_mock.assert_called_once() ================================================ FILE: tests/apps/account/test_change_email.py ================================================ import json from http import HTTPStatus from unittest.mock import patch from django.contrib.auth import get_user_model from django.urls import reverse import pytest from pytest_django.asserts import assertTemplateNotUsed, assertTemplateUsed from allauth.account.app_settings import LoginMethod from allauth.account.models import EmailAddress, EmailConfirmationHMAC from allauth.account.utils import user_email def test_ajax_get(auth_client, user): primary = EmailAddress.objects.filter(user=user).first() secondary = EmailAddress.objects.create( email="secondary@email.org", user=user, verified=False, primary=False ) resp = auth_client.get( reverse("account_email"), HTTP_X_REQUESTED_WITH="XMLHttpRequest" ) data = json.loads(resp.content.decode("utf8")) assert data["data"] == [ { "id": primary.pk, "email": primary.email, "primary": True, "verified": True, }, { "id": secondary.pk, "email": secondary.email, "primary": False, "verified": False, }, ] def test_ajax_add(auth_client): resp = auth_client.post( reverse("account_email"), {"action_add": "", "email": "john3@example.org"}, HTTP_X_REQUESTED_WITH="XMLHttpRequest", ) data = json.loads(resp.content.decode("utf8")) assert data["location"] == reverse("account_email") def test_ajax_add_invalid(auth_client): resp = auth_client.post( reverse("account_email"), {"action_add": "", "email": "john3#example.org"}, HTTP_X_REQUESTED_WITH="XMLHttpRequest", ) data = json.loads(resp.content.decode("utf8")) assert "valid" in data["form"]["fields"]["email"]["errors"][0] def test_ajax_remove_primary(auth_client, user, settings): settings.ACCOUNT_LOGIN_METHODS = {"email"} resp = auth_client.post( reverse("account_email"), {"action_remove": "", "email": user.email}, HTTP_X_REQUESTED_WITH="XMLHttpRequest", ) assertTemplateUsed(resp, "account/messages/cannot_delete_primary_email.txt") data = json.loads(resp.content.decode("utf8")) assert data["location"] == reverse("account_email") def test_remove_secondary(auth_client, user, settings, mailoutbox): settings.ACCOUNT_EMAIL_NOTIFICATIONS = True secondary = EmailAddress.objects.create( email="secondary@email.org", user=user, verified=False, primary=False ) resp = auth_client.post( reverse("account_email"), {"action_remove": "", "email": secondary.email}, ) assert not EmailAddress.objects.filter(pk=secondary.pk).exists() assertTemplateUsed(resp, "account/messages/email_deleted.txt") assert len(mailoutbox) == 1 assert f"{secondary.email} has been removed" in mailoutbox[0].body def test_set_primary_unverified(auth_client, user): secondary = EmailAddress.objects.create( email="secondary@email.org", user=user, verified=False, primary=False ) resp = auth_client.post( reverse("account_email"), {"action_primary": "", "email": secondary.email}, ) primary = EmailAddress.objects.get(email=user.email) secondary.refresh_from_db() assert not secondary.primary assert primary.primary assertTemplateUsed(resp, "account/messages/unverified_primary_email.txt") def test_set_primary(auth_client, user): primary = EmailAddress.objects.get(email=user.email) secondary = EmailAddress.objects.create( email="secondary@email.org", user=user, verified=True, primary=False ) resp = auth_client.post( reverse("account_email"), {"action_primary": "", "email": secondary.email}, ) primary.refresh_from_db() secondary.refresh_from_db() assert not primary.primary assert secondary.primary assertTemplateUsed(resp, "account/messages/primary_email_set.txt") def test_verify(auth_client, user): secondary = EmailAddress.objects.create( email="secondary@email.org", user=user, verified=False, primary=False ) resp = auth_client.post( reverse("account_email"), {"action_send": "", "email": secondary.email}, ) assertTemplateUsed(resp, "account/messages/email_confirmation_sent.txt") def test_verify_unknown_email(auth_client, user): auth_client.post( reverse("account_email"), {"action_send": "", "email": "email@unknown.org"}, ) # This unknown email address must not be implicitly added. assert EmailAddress.objects.filter(user=user).count() == 1 def test_add_with_two_limiter(auth_client, user, settings): EmailAddress.objects.create( email="secondary@email.org", user=user, verified=False, primary=False ) settings.ACCOUNT_MAX_EMAIL_ADDRESSES = 2 resp = auth_client.post( reverse("account_email"), {"action_add": "", "email": "john3@example.org"} ) assertTemplateNotUsed(resp, "account/messages/email_confirmation_sent.txt") def test_add_with_none_limiter(auth_client, settings): settings.ACCOUNT_MAX_EMAIL_ADDRESSES = None resp = auth_client.post( reverse("account_email"), {"action_add": "", "email": "john3@example.org"} ) assertTemplateUsed(resp, "account/messages/email_confirmation_sent.txt") def test_add_with_zero_limiter(auth_client, settings): settings.ACCOUNT_MAX_EMAIL_ADDRESSES = 0 resp = auth_client.post( reverse("account_email"), {"action_add": "", "email": "john3@example.org"} ) assertTemplateUsed(resp, "account/messages/email_confirmation_sent.txt") @pytest.mark.parametrize("has_email_field", [True, False]) def test_set_email_as_primary_doesnt_override_existing_changes_on_the_user( db, has_email_field, settings ): if not has_email_field: settings.ACCOUNT_USER_MODEL_EMAIL_FIELD = None user = get_user_model().objects.create( username="@raymond.penners", first_name="Before Update" ) email = EmailAddress.objects.create( user=user, email="raymond.penners@example.com", primary=True, verified=True, ) updated_first_name = "Updated" get_user_model().objects.filter(id=user.id).update(first_name=updated_first_name) email.set_as_primary() user.refresh_from_db() assert user.first_name == updated_first_name def test_delete_email_changes_user_email(user_factory, client, email_factory): user = user_factory(email_verified=False) client.force_login(user) first_email = EmailAddress.objects.get(user=user) first_email.primary = False first_email.save() # other_unverified_email EmailAddress.objects.create( user=user, email=email_factory(), verified=False, primary=False ) other_verified_email = EmailAddress.objects.create( user=user, email=email_factory(), verified=True, primary=False ) assert user_email(user) == first_email.email resp = client.post( reverse("account_email"), {"action_remove": "", "email": first_email.email}, ) assert resp.status_code == HTTPStatus.FOUND user.refresh_from_db() assert user_email(user) == other_verified_email.email def test_delete_email_wipes_user_email(user_factory, client): user = user_factory(email_verified=False) client.force_login(user) first_email = EmailAddress.objects.get(user=user) first_email.primary = False first_email.save() assert user_email(user) == first_email.email resp = client.post( reverse("account_email"), {"action_remove": "", "email": first_email.email}, ) assert resp.status_code == HTTPStatus.FOUND user.refresh_from_db() assert user_email(user) == "" def test_change_email(user_factory, client, settings, mailoutbox): settings.ACCOUNT_CHANGE_EMAIL = True settings.ACCOUNT_EMAIL_CONFIRMATION_HMAC = True settings.ACCOUNT_EMAIL_NOTIFICATIONS = True user = user_factory(email_verified=True) client.force_login(user) current_email = EmailAddress.objects.get(user=user) resp = client.post( reverse("account_email"), {"action_add": "", "email": "change-to@this.org"}, ) assert resp.status_code == HTTPStatus.FOUND assert len(mailoutbox) == 1 assert mailoutbox[0].subject == "[example.com] Please Confirm Your Email Address" new_email = EmailAddress.objects.get(email="change-to@this.org") key = EmailConfirmationHMAC(new_email).key with patch("allauth.account.signals.email_changed.send") as email_changed_mock: resp = client.post(reverse("account_confirm_email", args=[key])) assert resp.status_code == HTTPStatus.FOUND assert not EmailAddress.objects.filter(pk=current_email.pk).exists() assert EmailAddress.objects.filter(user=user).count() == 1 new_email.refresh_from_db() assert new_email.verified assert new_email.primary assert email_changed_mock.called assert len(mailoutbox) == 2 assert mailoutbox[1].subject == "[example.com] Email Changed" assert mailoutbox[1].to == [user.email] def test_add(auth_client, user, settings): resp = auth_client.post( reverse("account_email"), {"action_add": "", "email": "john3@example.org"}, ) EmailAddress.objects.get( email="john3@example.org", user=user, verified=False, primary=False, ) assertTemplateUsed(resp, "account/messages/email_confirmation_sent.txt") def test_add_with_reauthentication(auth_client, user, user_password, settings): settings.ACCOUNT_REAUTHENTICATION_REQUIRED = True resp = auth_client.post( reverse("account_email"), {"action_add": "", "email": "john3@example.org"}, ) assert not EmailAddress.objects.filter(email="john3@example.org").exists() assert resp.status_code == HTTPStatus.FOUND assert ( resp["location"] == f"{reverse('account_reauthenticate')}?next=%2Faccounts%2Femail%2F" ) resp = auth_client.post(resp["location"], {"password": user_password}) assert EmailAddress.objects.filter(email="john3@example.org").exists() assertTemplateUsed(resp, "account/messages/email_confirmation_sent.txt") assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_email") @pytest.mark.parametrize( "prevent_enumeration", [ False, True, "strict", ], ) def test_add_not_allowed( auth_client, user, settings, user_factory, prevent_enumeration ): settings.ACCOUNT_PREVENT_ENUMERATION = prevent_enumeration email = "inuse@byotheruser.com" user_factory(email=email) resp = auth_client.post( reverse("account_email"), {"action_add": "", "email": email}, ) if prevent_enumeration: assert resp.status_code == HTTPStatus.FOUND email_address = EmailAddress.objects.get( email=email, user=user, verified=False, primary=False, ) assertTemplateUsed(resp, "account/messages/email_confirmation_sent.txt") key = EmailConfirmationHMAC(email_address).key resp = auth_client.post(reverse("account_confirm_email", args=[key])) assertTemplateUsed(resp, "account/messages/email_confirmation_failed.txt") assert resp.status_code == HTTPStatus.FOUND email_address.refresh_from_db() assert not email_address.verified else: assert resp.status_code == HTTPStatus.OK assert resp.context["form"].errors == { "email": ["A user is already registered with this email address."] } @pytest.mark.parametrize( "login_methods,primary_email,secondary_emails,delete_email,success", [ ({LoginMethod.EMAIL}, "pri@ma.il", ["sec@ma.il"], "pri@ma.il", False), ({LoginMethod.EMAIL}, "pri@ma.il", ["sec@ma.il"], "sec@ma.il", True), ({LoginMethod.EMAIL}, "pri@ma.il", [], "pri@ma.il", False), ({LoginMethod.USERNAME}, "pri@ma.il", ["sec@ma.il"], "pri@ma.il", False), ({LoginMethod.USERNAME}, "pri@ma.il", ["sec@ma.il"], "sec@ma.il", True), ({LoginMethod.USERNAME}, "pri@ma.il", [], "pri@ma.il", True), ( {LoginMethod.USERNAME, LoginMethod.EMAIL}, "pri@ma.il", ["sec@ma.il"], "pri@ma.il", False, ), ( {LoginMethod.USERNAME, LoginMethod.EMAIL}, "pri@ma.il", ["sec@ma.il"], "sec@ma.il", True, ), ({LoginMethod.USERNAME, LoginMethod.EMAIL}, "pri@ma.il", [], "pri@ma.il", True), ], ) def test_remove_email( client, settings, user_factory, primary_email, secondary_emails, delete_email, login_methods, success, ): settings.ACCOUNT_LOGIN_METHODS = login_methods user = user_factory(email=primary_email) EmailAddress.objects.bulk_create( [ EmailAddress(user=user, email=email, primary=False, verified=False) for email in secondary_emails ] ) client.force_login(user) resp = client.post( reverse("account_email"), {"action_remove": "", "email": delete_email}, ) assert EmailAddress.objects.filter(email=delete_email).exists() == (not success) if not success: assertTemplateUsed(resp, "account/messages/cannot_delete_primary_email.txt") @pytest.mark.parametrize( "email,did_look_up", [ ("valid@email.org", True), ("not-an-email", False), ], ) def test_dont_lookup_invalid_email(auth_client, email, did_look_up): with patch("allauth.account.views.EmailAddress.objects.get_for_user") as gfu_mock: gfu_mock.side_effect = EmailAddress.DoesNotExist auth_client.post( reverse("account_email"), {"action_remove": "", "email": email}, ) assert gfu_mock.called == did_look_up def test_add_requires_reauthentication(settings, auth_client): settings.ACCOUNT_REAUTHENTICATION_REQUIRED = True resp = auth_client.post( reverse("account_email"), {"action_add": "", "email": "john3@example.org"}, ) assert not EmailAddress.objects.filter(email="john3@example.org").exists() assert resp["location"].startswith(reverse("account_reauthenticate")) def test_remove_requires_reauthentication(auth_client, user, settings): settings.ACCOUNT_REAUTHENTICATION_REQUIRED = True secondary = EmailAddress.objects.create( email="secondary@email.org", user=user, verified=False, primary=False ) resp = auth_client.post( reverse("account_email"), {"action_remove": "", "email": secondary.email}, ) assert resp["location"].startswith(reverse("account_reauthenticate")) assert EmailAddress.objects.filter(pk=secondary.pk).exists() def test_set_primary_requires_reauthentication(auth_client, user, settings): settings.ACCOUNT_REAUTHENTICATION_REQUIRED = True primary = EmailAddress.objects.get(email=user.email) secondary = EmailAddress.objects.create( email="secondary@email.org", user=user, verified=True, primary=False ) resp = auth_client.post( reverse("account_email"), {"action_primary": "", "email": secondary.email}, ) assert resp["location"].startswith(reverse("account_reauthenticate")) primary.refresh_from_db() secondary.refresh_from_db() assert primary.primary assert not secondary.primary def test_change_email_links_restrict_email(user_factory, client, settings, mailoutbox): settings.ACCOUNT_CHANGE_EMAIL = True settings.ACCOUNT_EMAIL_CONFIRMATION_HMAC = True settings.ACCOUNT_EMAIL_NOTIFICATIONS = False user = user_factory(email_verified=True) client.force_login(user) # Change the email... resp = client.post( reverse("account_email"), {"action_add": "", "email": "change-to@this.org"}, ) assert resp.status_code == HTTPStatus.FOUND # Extract the verification link from the outbox. assert len(mailoutbox) == 1 assert mailoutbox[-1].to[0] == "change-to@this.org" assert mailoutbox[-1].subject == "[example.com] Please Confirm Your Email Address" verify_link_1 = ( mailoutbox[-1].body[mailoutbox[-1].body.find("http://") :].split()[0] ) # Without verifying, change it once more. resp = client.post( reverse("account_email"), {"action_add": "", "email": "change-to@that.org"}, ) assert resp.status_code == HTTPStatus.FOUND # And, extract the 2nd link. assert len(mailoutbox) == 2 assert mailoutbox[-1].to[0] == "change-to@that.org" assert mailoutbox[-1].subject == "[example.com] Please Confirm Your Email Address" verify_link_2 = ( mailoutbox[-1].body[mailoutbox[-1].body.find("http://") :].split()[0] ) # Try and verify the first link. That should no longer be possible. resp = client.post(verify_link_1) assert resp.status_code == HTTPStatus.NOT_FOUND # Confirm that the POST above had no effect. assert EmailAddress.objects.filter(user=user).count() == 2 email_address = EmailAddress.objects.get(user=user, verified=True) assert email_address.email == user.email email_address = EmailAddress.objects.get(user=user, verified=False) assert email_address.email == "change-to@that.org" # Use the 2nd verification link resp = client.post(verify_link_2) assert resp.status_code == HTTPStatus.FOUND # That one works. assert EmailAddress.objects.filter(user=user).count() == 1 email_address = EmailAddress.objects.get(user=user) assert email_address.verified assert email_address.email == "change-to@that.org" def test_change_unverified_email(user_factory, client, settings, mailoutbox): settings.ACCOUNT_CHANGE_EMAIL = True settings.ACCOUNT_EMAIL_CONFIRMATION_HMAC = True settings.ACCOUNT_EMAIL_NOTIFICATIONS = True user = user_factory(email_verified=False) client.force_login(user) current_email = EmailAddress.objects.get(user=user) assert user.email == current_email.email resp = client.post( reverse("account_email"), {"action_add": "", "email": "change-to@this.org"}, ) assert resp.status_code == HTTPStatus.FOUND assert len(mailoutbox) == 1 assert not EmailAddress.objects.filter(pk=current_email.pk).exists() user.refresh_from_db() assert user.email == "" assert mailoutbox[0].subject == "[example.com] Please Confirm Your Email Address" new_email = EmailAddress.objects.get(email="change-to@this.org") key = EmailConfirmationHMAC(new_email).key with patch("allauth.account.signals.email_changed.send") as email_changed_mock: resp = client.post(reverse("account_confirm_email", args=[key])) assert resp.status_code == HTTPStatus.FOUND assert not EmailAddress.objects.filter(pk=current_email.pk).exists() assert EmailAddress.objects.filter(user=user).count() == 1 new_email.refresh_from_db() assert new_email.verified assert new_email.primary assert email_changed_mock.called def test_change_unverified_email_to_verified_email_of_other_user( user_factory, client, settings, mailoutbox ): settings.ACCOUNT_CHANGE_EMAIL = True settings.ACCOUNT_EMAIL_CONFIRMATION_HMAC = True settings.ACCOUNT_EMAIL_NOTIFICATIONS = True verified_user = user_factory(email_verified=True) user = user_factory(email_verified=False) client.force_login(user) current_email = EmailAddress.objects.get(user=user) assert user.email == current_email.email resp = client.post( reverse("account_email"), {"action_add": "", "email": verified_user.email}, ) assert resp.status_code == HTTPStatus.FOUND assert len(mailoutbox) == 1 assert not EmailAddress.objects.filter(pk=current_email.pk).exists() user.refresh_from_db() assert user.email == "" assert mailoutbox[0].subject == "[example.com] Please Confirm Your Email Address" new_email = EmailAddress.objects.get( user=user, verified=False, email=verified_user.email ) key = EmailConfirmationHMAC(new_email).key with patch("allauth.account.signals.email_changed.send") as email_changed_mock: resp = client.post(reverse("account_confirm_email", args=[key])) assert resp.status_code == HTTPStatus.FOUND last_message = list(resp.context["messages"])[-1] assert last_message.message.startswith("Unable to confirm") assert EmailAddress.objects.filter(user=user).count() == 1 new_email.refresh_from_db() assert not new_email.verified assert not email_changed_mock.called ================================================ FILE: tests/apps/account/test_change_password.py ================================================ from http import HTTPStatus from django.urls import reverse, reverse_lazy import pytest def test_change_unusable_password_redirects_to_set(client, user, user_password): user.set_unusable_password() user.save() client.force_login(user) resp = client.get(reverse("account_change_password")) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_set_password") def test_set_usable_password_redirects_to_change(auth_client, user): resp = auth_client.get(reverse("account_set_password")) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_change_password") @pytest.mark.parametrize( "logout,next_url,redirect_chain", [ (False, "", [(reverse_lazy("account_change_password"), HTTPStatus.FOUND)]), (False, "/foo", [("/foo", HTTPStatus.FOUND)]), ( True, "", [ (reverse_lazy("account_change_password"), HTTPStatus.FOUND), ( "/accounts/login/?next=/accounts/password/change/", HTTPStatus.FOUND, ), ], ), (True, "/foo", [("/foo", HTTPStatus.FOUND)]), ], ) def test_set_password( client, user, next_url, password_factory, logout, settings, redirect_chain ): settings.ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = logout user.set_unusable_password() user.save() client.force_login(user) password = password_factory() data = {"password1": password, "password2": password} if next_url: data["next"] = next_url resp = client.post( reverse("account_set_password"), data, follow=True, ) assert resp.redirect_chain == redirect_chain @pytest.mark.parametrize( "logout,next_url,redirect_chain", [ (False, "", [(reverse_lazy("account_change_password"), HTTPStatus.FOUND)]), (False, "/foo", [("/foo", HTTPStatus.FOUND)]), ( True, "", [ (reverse_lazy("account_change_password"), HTTPStatus.FOUND), ( "/accounts/login/?next=/accounts/password/change/", HTTPStatus.FOUND, ), ], ), (True, "/foo", [("/foo", HTTPStatus.FOUND)]), ], ) def test_change_password( auth_client, user, user_password, next_url, password_factory, logout, settings, redirect_chain, mailoutbox, ): settings.ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = logout settings.ACCOUNT_EMAIL_NOTIFICATIONS = True password = password_factory() data = {"oldpassword": user_password, "password1": password, "password2": password} if next_url: data["next"] = next_url resp = auth_client.post( reverse("account_change_password"), data, follow=True, ) assert resp.redirect_chain == redirect_chain assert len(mailoutbox) == 1 assert "Your password has been changed" in mailoutbox[0].body ================================================ FILE: tests/apps/account/test_commands.py ================================================ from django.core.management import call_command def test_unset_multipleprimaryemails(db): # This command needs to be dropped, in favor of having a conditional # constraint. call_command("account_unsetmultipleprimaryemails") ================================================ FILE: tests/apps/account/test_decorators.py ================================================ from http import HTTPStatus from django.urls import reverse from pytest_django.asserts import assertTemplateUsed from allauth.account.decorators import verified_email_required def test_verified_email_required(user_factory, request_factory): user = user_factory(email_verified=False) @verified_email_required def view(request): raise AssertionError() request = request_factory.get("/") request.user = user view(request) assertTemplateUsed("account/verified_email_required.html") def test_secure_admin_login_skips_admin_login_next(client): """ Test that we're not using 'next=/admin/login%2Fnext=/foo' """ resp = client.get(f"{reverse('admin:login')}?next=/foo") assert resp["location"] == "/accounts/login/?next=%2Ffoo" def test_secure_admin_login_denies_regular_users(auth_client): resp = auth_client.get(reverse("admin:login")) assert resp.status_code == HTTPStatus.FORBIDDEN def test_secure_admin_login_passes_staff(auth_client, user): user.is_staff = True user.is_superuser = True user.save(update_fields=["is_staff", "is_superuser"]) resp = auth_client.get(reverse("admin:auth_user_changelist")) assert resp.status_code == HTTPStatus.OK ================================================ FILE: tests/apps/account/test_email_verification.py ================================================ from datetime import timedelta from http import HTTPStatus from unittest.mock import Mock from django.contrib.auth import SESSION_KEY, get_user_model from django.core.cache import cache from django.urls import reverse from django.utils.timezone import now import pytest from pytest_django.asserts import ( assertRedirects, assertTemplateNotUsed, assertTemplateUsed, ) from allauth.account import app_settings from allauth.account.adapter import DefaultAccountAdapter from allauth.account.models import ( EmailAddress, EmailConfirmation, EmailConfirmationHMAC, ) from allauth.account.signals import user_logged_in class SignupRedirectEmailVerificationAdapter(DefaultAccountAdapter): SIGNUP_REDIRECT_URL = "/foobar" def get_signup_redirect_url(self, request): return self.SIGNUP_REDIRECT_URL @pytest.mark.parametrize( "adapter,query,expected_location", [ (None, "", app_settings.SIGNUP_REDIRECT_URL), (None, "?next=/foo", "/foo"), ( "tests.apps.account.test_email_verification.SignupRedirectEmailVerificationAdapter", "", SignupRedirectEmailVerificationAdapter.SIGNUP_REDIRECT_URL, ), ], ) def test_login_on_verification( adapter, client, db, query, expected_location, password_factory, settings ): settings.ACCOUNT_EMAIL_VERIFICATION = app_settings.EmailVerificationMethod.MANDATORY settings.ACCOUNT_EMAIL_CONFIRMATION_HMAC = True settings.ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True if adapter: settings.ACCOUNT_ADAPTER = adapter password = password_factory() resp = client.post( reverse("account_signup"), data={ "username": "john", "email": "a@a.com", "password1": password, "password2": password, }, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_email_verification_sent") resp = client.get(resp["location"]) assert resp.status_code == HTTPStatus.OK email = EmailAddress.objects.get(email="a@a.com") key = EmailConfirmationHMAC(email).key receiver_mock = Mock() # we've logged if signal was called user_logged_in.connect(receiver_mock) resp = client.post(reverse("account_confirm_email", args=[key]) + query) assert resp["location"] == expected_location email = EmailAddress.objects.get(pk=email.pk) assert email.verified receiver_mock.assert_called_once_with( sender=get_user_model(), request=resp.wsgi_request, response=resp, user=email.user, signal=user_logged_in, ) user_logged_in.disconnect(receiver_mock) def test_email_verification_failed(settings, user_factory, client): settings.ACCOUNT_EMAIL_CONFIRMATION_HMAC = False user_factory(email_verified=True, email="foo@bar.org") unverified_user = user_factory(email_verified=False, email="foo@bar.org") email_address = EmailAddress.objects.get_for_user( unverified_user, unverified_user.email ) assert not email_address.verified confirmation = EmailConfirmation.objects.create( email_address=email_address, key="dummy", sent=now(), ) resp = client.post(reverse("account_confirm_email", args=[confirmation.key])) assertTemplateUsed(resp, "account/messages/email_confirmation_failed.txt") def test_email_verification_mandatory(settings, db, client, mailoutbox, enable_cache): settings.ACCOUNT_EMAIL_CONFIRMATION_HMAC = False settings.ACCOUNT_EMAIL_CONFIRMATION_COOLDOWN = 10 settings.ACCOUNT_EMAIL_VERIFICATION = app_settings.EmailVerificationMethod.MANDATORY # Signup resp = client.post( reverse("account_signup"), { "username": "johndoe", "email": "john@example.com", "password1": "johndoe", "password2": "johndoe", }, follow=True, ) assert resp.status_code == HTTPStatus.OK assert mailoutbox[0].to == ["john@example.com"] assert mailoutbox[0].body.find("http://") > 0 assert len(mailoutbox) == 1 assertTemplateUsed( resp, f"account/verification_sent.{app_settings.TEMPLATE_EXTENSION}", ) # Attempt to login, unverified for attempt in [1, 2]: resp = client.post( reverse("account_login"), {"login": "johndoe", "password": "johndoe"}, follow=True, ) # is_active is controlled by the admin to manually disable # users. I don't want this flag to flip automatically whenever # users verify their email addresses. assert ( get_user_model().objects.filter(username="johndoe", is_active=True).exists() ) assertTemplateUsed( resp, f"account/verification_sent.{app_settings.TEMPLATE_EXTENSION}", ) # Attempt 1: no mail is sent due to cool-down , # but there was already a mail in the outbox. assert len(mailoutbox) == attempt assert ( EmailConfirmation.objects.filter( email_address__email="john@example.com" ).count() == attempt ) # Wait for cooldown -- wipe cache (incl. rate limits) cache.clear() # if we don't wipe the session, login will redirect to pending stage... client.logout() # Verify, and re-attempt to login. confirmation = EmailConfirmation.objects.filter( email_address__user__username="johndoe" )[:1].get() resp = client.get(reverse("account_confirm_email", args=[confirmation.key])) assertTemplateUsed(resp, f"account/email_confirm.{app_settings.TEMPLATE_EXTENSION}") client.post(reverse("account_confirm_email", args=[confirmation.key])) resp = client.post( reverse("account_login"), {"login": "johndoe", "password": "johndoe"}, ) assertRedirects(resp, settings.LOGIN_REDIRECT_URL, fetch_redirect_response=False) def test_optional_email_verification(settings, client, db, mailoutbox): settings.ACCOUNT_SIGNUP_REDIRECT_URL = "/accounts/welcome/" settings.ACCOUNT_EMAIL_VERIFICATION = app_settings.EmailVerificationMethod.OPTIONAL settings.ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE = False # Signup client.get(reverse("account_signup")) resp = client.post( reverse("account_signup"), { "username": "johndoe", "email": "john@example.com", "password1": "johndoe", }, ) # Logged in assertRedirects( resp, settings.ACCOUNT_SIGNUP_REDIRECT_URL, fetch_redirect_response=False ) assert mailoutbox[0].to == ["john@example.com"] assert len(mailoutbox) == 1 # Logout & login again client.logout() # Wait for cooldown EmailConfirmation.objects.update(sent=now() - timedelta(days=1)) # Signup resp = client.post( reverse("account_login"), {"login": "johndoe", "password": "johndoe"}, ) assertRedirects(resp, settings.LOGIN_REDIRECT_URL, fetch_redirect_response=False) assert mailoutbox[0].to == ["john@example.com"] # There was an issue that we sent out email confirmation mails # on each login in case of optional verification. Make sure # this is not the case: assert len(mailoutbox) == 1 def test_email_verification_hmac(settings, client, user_factory, mailoutbox, rf): settings.ACCOUNT_EMAIL_CONFIRMATION_HMAC = True user = user_factory(email_verified=False) email = EmailAddress.objects.get_for_user(user, user.email) confirmation = EmailConfirmationHMAC(email) request = rf.get("/") confirmation.send(request=request) assert len(mailoutbox) == 1 client.post(reverse("account_confirm_email", args=[confirmation.key])) email = EmailAddress.objects.get(pk=email.pk) assert email.verified def test_email_verification_hmac_timeout( settings, user_factory, client, mailoutbox, rf ): settings.ACCOUNT_EMAIL_CONFIRMATION_HMAC = True settings.ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = 0 user = user_factory(email_verified=False) email = EmailAddress.objects.get_for_user(user, user.email) confirmation = EmailConfirmationHMAC(email) request = rf.get("/") confirmation.send(request=request) assert len(mailoutbox) == 1 client.post(reverse("account_confirm_email", args=[confirmation.key])) email = EmailAddress.objects.get(pk=email.pk) assert not email.verified def test_verify_email_with_another_user_logged_in( settings, user_factory, client, mailoutbox ): """Test the email verification view. If User B clicks on an email verification link while logged in as User A, ensure User A gets logged out.""" settings.ACCOUNT_LOGIN_METHODS = {app_settings.AuthenticationMethod.EMAIL} user = user_factory(email_verified=False) client.force_login(user) client.post(reverse("account_email"), {"email": user.email, "action_send": ""}) assert len(mailoutbox) == 1 assert mailoutbox[0].to == [user.email] client.logout() body = mailoutbox[0].body assert body.find("http://") > 0 user2 = user_factory(email_verified=False, password="doe") resp = client.post( reverse("account_login"), { "login": user2.email, "password": "doe", }, ) assert user2 == resp.context["user"] url = body[body.find("/accounts/confirm-email/") :].split()[0] resp = client.post(url) assertTemplateUsed(resp, "account/messages/logged_out.txt") assertTemplateUsed(resp, "account/messages/email_confirmed.txt") assertRedirects(resp, settings.LOGIN_URL, fetch_redirect_response=False) def test_verify_email_with_same_user_logged_in( settings, user_factory, client, mailoutbox ): """If the user clicks on an email verification link while logged in, ensure the user stays logged in. """ settings.ACCOUNT_LOGIN_METHODS = {app_settings.AuthenticationMethod.EMAIL} user = user_factory(email_verified=False) client.force_login(user) client.post(reverse("account_email"), {"email": user.email, "action_send": ""}) assert len(mailoutbox) == 1 assert mailoutbox[0].to == [user.email] body = mailoutbox[0].body assert body.find("http://") > 0 url = body[body.find("/accounts/confirm-email/") :].split()[0] resp = client.post(url) assertTemplateNotUsed(resp, "account/messages/logged_out.txt") assertTemplateUsed(resp, "account/messages/email_confirmed.txt") assertRedirects(resp, settings.LOGIN_REDIRECT_URL, fetch_redirect_response=False) assert user == resp.wsgi_request.user def test_verify_logs_out_user(auth_client, settings, user, user_factory): """ When a user is signed in, and you follow an email confirmation link of another user within the same browser/session, be sure to sign out the signed in user. """ settings.ACCOUNT_CONFIRM_EMAIL_ON_GET = False confirming_user = user_factory(email_verified=False) assert auth_client.session[SESSION_KEY] == str(user.pk) email = EmailAddress.objects.get(user=confirming_user, verified=False) auth_client.get( reverse( "account_confirm_email", kwargs={"key": EmailConfirmationHMAC(email).key} ) ) assert not auth_client.session.get(SESSION_KEY) def test_email_verification_login_redirect(client, db, settings, password_factory): settings.ACCOUNT_EMAIL_VERIFICATION = app_settings.EmailVerificationMethod.MANDATORY password = password_factory() resp = client.post( reverse("account_signup"), { "username": "johndoe", "email": "user@email.org", "password1": password, "password2": password, }, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_email_verification_sent") resp = client.get(reverse("account_login")) assert resp.status_code == HTTPStatus.OK def test_email_verification_redirect_url(client, db, settings, user_password): settings.ACCOUNT_EMAIL_VERIFICATION = app_settings.EmailVerificationMethod.MANDATORY settings.ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = "/foobar" settings.ACCOUNT_CONFIRM_EMAIL_ON_GET = True email = "user@email.org" resp = client.post( reverse("account_signup"), { "username": "johndoe", "email": email, "password1": user_password, "password2": user_password, }, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_email_verification_sent") confirmation = EmailConfirmationHMAC(EmailAddress.objects.get(email=email)) resp = client.get(reverse("account_confirm_email", args=[confirmation.key])) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == "/foobar" def test_verified_email_decorator__unverified(auth_client): EmailAddress.objects.all().update(verified=False) resp = auth_client.get(reverse("tests_account_check_verified_email")) assert resp.status_code == HTTPStatus.OK assertTemplateUsed(resp, "account/verified_email_required.html") def test_verified_email_decorator__verified(auth_client): EmailAddress.objects.all().update(verified=True) resp = auth_client.get(reverse("tests_account_check_verified_email")) assert resp.status_code == HTTPStatus.OK assert resp.content == b"VERIFIED" ================================================ FILE: tests/apps/account/test_email_verification_by_code.py ================================================ from http import HTTPStatus from unittest.mock import patch from django.conf import settings from django.contrib.auth import get_user_model from django.test import Client from django.urls import reverse import pytest from allauth.account.models import EmailAddress @pytest.fixture(autouse=True) def email_verification_settings(settings): settings.ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.ACCOUNT_AUTHENTICATION_METHOD = "email" return settings @pytest.mark.parametrize( "query,expected_url", [ ("", settings.LOGIN_REDIRECT_URL), ("?next=/foo", "/foo"), ], ) def test_signup( client, db, settings, password_factory, get_last_email_verification_code, query, expected_url, mailoutbox, ): password = password_factory() resp = client.post( reverse("account_signup") + query, { "username": "johndoe", "email": "john@example.com", "password1": password, "password2": password, }, ) assert get_user_model().objects.filter(username="johndoe").count() == 1 code = get_last_email_verification_code(client, mailoutbox) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_email_verification_sent") resp = client.get(reverse("account_email_verification_sent")) assert resp.status_code == HTTPStatus.OK resp = client.post(reverse("account_email_verification_sent"), data={"code": code}) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == expected_url def test_signup_prevent_enumeration( client, db, settings, password_factory, user, mailoutbox ): password = password_factory() resp = client.post( reverse("account_signup"), { "username": "johndoe", "email": user.email, "password1": password, "password2": password, }, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_email_verification_sent") assert not get_user_model().objects.filter(username="johndoe").exists() assert mailoutbox[0].subject == "[example.com] Account Already Exists" resp = client.get(reverse("account_email_verification_sent")) assert resp.status_code == HTTPStatus.OK resp = client.post(reverse("account_email_verification_sent"), data={"code": ""}) assert resp.status_code == HTTPStatus.OK assert resp.context["form"].errors == {"code": ["This field is required."]} resp = client.post(reverse("account_email_verification_sent"), data={"code": "123"}) assert resp.status_code == HTTPStatus.OK assert resp.context["form"].errors == {"code": ["Incorrect code."]} # Max attempts resp = client.post(reverse("account_email_verification_sent"), data={"code": "456"}) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_login") @pytest.mark.parametrize("change_email", (False, True)) def test_add_or_change_email( auth_client, user, get_last_email_verification_code, change_email, settings, mailoutbox, ): settings.ACCOUNT_CHANGE_EMAIL = change_email settings.ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_RESEND = True email = "additional@email.org" assert EmailAddress.objects.filter(user=user).count() == 1 with patch("allauth.account.signals.email_added") as email_added_signal: with patch("allauth.account.signals.email_changed") as email_changed_signal: resp = auth_client.post( reverse("account_email"), {"action_add": "", "email": email} ) assert resp["location"] == reverse("account_email_verification_sent") assert not email_added_signal.send.called assert not email_changed_signal.send.called assert EmailAddress.objects.filter(email=email).count() == 0 code = get_last_email_verification_code(auth_client, mailoutbox) resp = auth_client.get(reverse("account_email_verification_sent")) assert resp.status_code == HTTPStatus.OK resp = auth_client.post( reverse("account_email_verification_sent"), {"action": "resend"} ) assert EmailAddress.objects.filter(email=email).count() == 0 assert resp.status_code == HTTPStatus.FOUND code2 = get_last_email_verification_code(auth_client, mailoutbox) assert code != code2 with patch("allauth.account.signals.email_added") as email_added_signal: with patch("allauth.account.signals.email_changed") as email_changed_signal: with patch( "allauth.account.signals.email_confirmed" ) as email_confirmed_signal: resp = auth_client.post( reverse("account_email_verification_sent"), data={"code": code2} ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == settings.LOGIN_REDIRECT_URL assert email_added_signal.send.called assert email_confirmed_signal.send.called assert email_changed_signal.send.called == change_email assert EmailAddress.objects.filter(email=email, verified=True).count() == 1 assert EmailAddress.objects.filter(user=user).count() == (1 if change_email else 2) def test_email_verification_login_redirect( client, db, settings, password_factory, email_verification_settings ): password = password_factory() resp = client.post( reverse("account_signup"), { "username": "johndoe", "email": "user@email.org", "password1": password, "password2": password, }, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_email_verification_sent") resp = client.get(reverse("account_login")) assert resp["location"] == reverse("account_email_verification_sent") def test_email_verification_rate_limits( db, user_password, email_verification_settings, settings, user_factory, password_factory, enable_cache, ): settings.ACCOUNT_RATE_LIMITS = {"confirm_email": "1/m/key"} email = "user@email.org" user_factory(email=email, email_verified=False, password=user_password) for attempt in range(2): resp = Client().post( reverse("account_login"), { "login": email, "password": user_password, }, ) if attempt == 0: assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_email_verification_sent") else: assert resp.status_code == HTTPStatus.OK assert resp.context["form"].errors == { "__all__": ["Too many failed login attempts. Try again later."] } def test_change_email_vs_enumeration_prevention( client, db, settings, password_factory, get_last_email_verification_code, mailoutbox, messagesoutbox, user, ): settings.ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_RESEND = True password = password_factory() resp = client.post( reverse("account_signup"), { "username": "johndoe", "email": user.email, "password1": password, "password2": password, }, ) # No user signed up. assert get_user_model().objects.filter(username="johndoe").count() == 0 assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_email_verification_sent") assert len(mailoutbox) == 1 assert mailoutbox[0].subject == "[example.com] Account Already Exists" assert len(messagesoutbox) == 1 assert ( messagesoutbox[-1]["message_template"] == "account/messages/email_confirmation_sent.txt" ) # Resend resp = client.post(reverse("account_email_verification_sent"), {"action": "resend"}) assert len(mailoutbox) == 1 assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_email_verification_sent") # No new emails sent. assert len(mailoutbox) == 1 # Yet, pretend we did. assert len(messagesoutbox) == 2 assert ( messagesoutbox[-1]["message_template"] == "account/messages/email_confirmation_sent.txt" ) def test_change_to_already_existing_email( client, db, settings, password_factory, get_last_email_verification_code, mailoutbox, messagesoutbox, user, ): settings.ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_RESEND = True settings.ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_CHANGE = True password = password_factory() resp = client.post( reverse("account_signup"), { "username": "johndoe", "email": "new@user.org", "password1": password, "password2": password, }, ) # A user signed up. new_user = get_user_model().objects.get(username="johndoe") assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_email_verification_sent") assert len(mailoutbox) == 1 assert mailoutbox[0].subject == "[example.com] Please Confirm Your Email Address" assert len(messagesoutbox) == 1 assert ( messagesoutbox[-1]["message_template"] == "account/messages/email_confirmation_sent.txt" ) # Change to a conflicting email resp = client.post( reverse("account_email_verification_sent"), {"action": "change", "email": user.email}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_email_verification_sent") assert len(mailoutbox) == 2 assert mailoutbox[1].subject == "[example.com] Account Already Exists" assert len(messagesoutbox) == 2 assert ( messagesoutbox[-1]["message_template"] == "account/messages/email_confirmation_sent.txt" ) new_user.refresh_from_db() assert new_user.email == "new@user.org" # Change back to new email resp = client.post( reverse("account_email_verification_sent"), {"action": "change", "email": "new2@user.org"}, ) assert len(mailoutbox) == 3 assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_email_verification_sent") assert mailoutbox[2].subject == "[example.com] Please Confirm Your Email Address" assert len(messagesoutbox) == 3 assert ( messagesoutbox[-1]["message_template"] == "account/messages/email_confirmation_sent.txt" ) new_user.refresh_from_db() assert new_user.email == "new2@user.org" code = get_last_email_verification_code(client, mailoutbox) resp = client.post(reverse("account_email_verification_sent"), data={"code": code}) assert resp.status_code == HTTPStatus.FOUND email_address = EmailAddress.objects.filter(user=new_user).get() assert email_address.verified assert email_address.email == "new2@user.org" def test_verified_email_decorator__unverified( auth_client, mailoutbox, get_last_email_verification_code ): EmailAddress.objects.all().update(verified=False) resp = auth_client.get(reverse("tests_account_check_verified_email")) assert resp.status_code == HTTPStatus.FOUND assert resp["location"].startswith(reverse("account_email_verification_sent")) code = get_last_email_verification_code(auth_client, mailoutbox) resp = auth_client.post( reverse("account_email_verification_sent"), data={"code": code, "next": reverse("tests_account_check_verified_email")}, follow=True, ) assert resp.status_code == HTTPStatus.OK assert resp.content == b"VERIFIED" ================================================ FILE: tests/apps/account/test_login.py ================================================ import json from http import HTTPStatus from unittest.mock import ANY, patch from django.conf import settings from django.contrib.auth import get_user_model from django.core import mail from django.test import TestCase from django.test.utils import override_settings from django.urls import NoReverseMatch, reverse from pytest_django.asserts import assertTemplateUsed from allauth.account import app_settings from allauth.account.authentication import AUTHENTICATION_METHODS_SESSION_KEY from allauth.account.forms import LoginForm from allauth.account.models import EmailAddress @override_settings( ACCOUNT_DEFAULT_HTTP_PROTOCOL="https", ACCOUNT_EMAIL_VERIFICATION=app_settings.EmailVerificationMethod.MANDATORY, ACCOUNT_LOGIN_METHODS={app_settings.AuthenticationMethod.USERNAME}, ACCOUNT_SIGNUP_FORM_CLASS=None, ACCOUNT_EMAIL_SUBJECT_PREFIX=None, LOGIN_REDIRECT_URL="/accounts/profile/", ACCOUNT_SIGNUP_REDIRECT_URL="/accounts/welcome/", ACCOUNT_ADAPTER="allauth.account.adapter.DefaultAccountAdapter", ACCOUNT_USERNAME_REQUIRED=True, ) class LoginTests(TestCase): @override_settings( ACCOUNT_LOGIN_METHODS={ app_settings.LoginMethod.USERNAME, app_settings.LoginMethod.EMAIL, } ) def test_username_containing_at(self): user = get_user_model().objects.create(username="@raymond.penners") user.set_password("psst") user.save() EmailAddress.objects.create( user=user, email="raymond.penners@example.com", primary=True, verified=True, ) resp = self.client.post( reverse("account_login"), {"login": "@raymond.penners", "password": "psst"}, ) self.assertRedirects( resp, settings.LOGIN_REDIRECT_URL, fetch_redirect_response=False ) self.assertEqual( self.client.session[AUTHENTICATION_METHODS_SESSION_KEY], [ { "at": ANY, "username": "@raymond.penners", "method": "password", } ], ) def _create_user(self, username="john", password="doe", **kwargs): user = get_user_model().objects.create( username=username, is_active=True, **kwargs ) if password: user.set_password(password) else: user.set_unusable_password() user.save() return user def _create_user_and_login(self, usable_password=True): password = "doe" if usable_password else False user = self._create_user(password=password) self.client.force_login(user) return user def test_redirect_when_authenticated(self): self._create_user_and_login() c = self.client resp = c.get(reverse("account_login")) self.assertRedirects(resp, "/accounts/profile/", fetch_redirect_response=False) def test_ajax_password_change(self): self._create_user_and_login() resp = self.client.post( reverse("account_change_password"), data={ "oldpassword": "doe", "password1": "AbCdEf!123", "password2": "AbCdEf!123456", }, HTTP_X_REQUESTED_WITH="XMLHttpRequest", ) self.assertEqual(resp["content-type"], "application/json") data = json.loads(resp.content.decode("utf8")) assert "same password" in data["form"]["fields"]["password2"]["errors"][0] @override_settings( ACCOUNT_EMAIL_VERIFICATION=app_settings.EmailVerificationMethod.OPTIONAL ) def test_login_unverified_account_optional(self): """Tests login behavior when email verification is optional.""" user = get_user_model().objects.create(username="john") user.set_password("doe") user.save() EmailAddress.objects.create( user=user, email="user@example.com", primary=True, verified=False ) resp = self.client.post( reverse("account_login"), {"login": "john", "password": "doe"} ) self.assertRedirects( resp, settings.LOGIN_REDIRECT_URL, fetch_redirect_response=False ) @override_settings( ACCOUNT_EMAIL_VERIFICATION=app_settings.EmailVerificationMethod.OPTIONAL, ACCOUNT_LOGIN_ATTEMPTS_LIMIT=3, CACHES={ "default": { "BACKEND": "django.core.cache.backends.locmem.LocMemCache", } }, ) def test_login_failed_attempts_exceeded(self): user = get_user_model().objects.create(username="john") user.set_password("doe") user.save() EmailAddress.objects.create( user=user, email="user@example.com", primary=True, verified=False ) for i in range(5): is_valid_attempt = i == 4 is_locked = i >= 3 resp = self.client.post( reverse("account_login"), { "login": ["john", "John", "JOHN", "JOhn", "joHN"][i], "password": ("doe" if is_valid_attempt else "wrong"), }, ) self.assertFormError( resp.context["form"], None, ( "Too many failed login attempts. Try again later." if is_locked else "The username and/or password you specified are not correct." ), ) @override_settings( ACCOUNT_LOGIN_METHODS={app_settings.LoginMethod.EMAIL}, ACCOUNT_EMAIL_VERIFICATION=app_settings.EmailVerificationMethod.MANDATORY, ACCOUNT_LOGIN_ATTEMPTS_LIMIT=1, CACHES={ "default": { "BACKEND": "django.core.cache.backends.locmem.LocMemCache", } }, ) def test_login_failed_attempts_exceeded_cleared_on_password_reset(self): # Ensure that login attempts, once they hit the limit, # can use the password reset mechanism to regain access. user = get_user_model().objects.create( username="john", email="john@example.org", is_active=True ) user.set_password("doe") user.save() EmailAddress.objects.create( user=user, email="john@example.org", primary=True, verified=True ) resp = self.client.post( reverse("account_login"), {"login": user.email, "password": "bad"} ) self.assertFormError( resp.context["form"], None, "The email address and/or password you specified are not correct.", ) resp = self.client.post( reverse("account_login"), {"login": user.email, "password": "bad"} ) self.assertFormError( resp.context["form"], None, "Too many failed login attempts. Try again later.", ) self.client.post(reverse("account_reset_password"), data={"email": user.email}) body = mail.outbox[0].body self.assertGreater(body.find("https://"), 0) # Extract URL for `password_reset_from_key` view and access it url = body[body.find("/accounts/password/reset/") :].split()[0] resp = self.client.get(url) # Follow the redirect the actual password reset page with the key # hidden. url = resp.url resp = self.client.get(url) self.assertTemplateUsed( resp, f"account/password_reset_from_key.{app_settings.TEMPLATE_EXTENSION}", ) self.assertFalse("token_fail" in resp.context_data) new_password = "newpass123" # Reset the password resp = self.client.post( url, {"password1": new_password, "password2": new_password} ) self.assertRedirects(resp, reverse("account_reset_password_from_key_done")) # Check the new password is in effect user = get_user_model().objects.get(pk=user.pk) self.assertTrue(user.check_password(new_password)) resp = self.client.post( reverse("account_login"), {"login": user.email, "password": new_password}, ) self.assertRedirects( resp, settings.LOGIN_REDIRECT_URL, fetch_redirect_response=False ) @override_settings( ACCOUNT_LOGIN_METHODS={app_settings.LoginMethod.EMAIL}, ACCOUNT_EMAIL_VERIFICATION=app_settings.EmailVerificationMethod.MANDATORY, ACCOUNT_LOGIN_ATTEMPTS_LIMIT=1, ) def test_login_using_unverified_email_address_is_prohibited(self): user = get_user_model().objects.create( username="john", email="john@example.org", is_active=True ) user.set_password("doe") user.save() EmailAddress.objects.create( user=user, email="john@example.org", primary=True, verified=True ) EmailAddress.objects.create( user=user, email="john@example.com", primary=False, verified=False ) resp = self.client.post( reverse("account_login"), {"login": "john@example.com", "password": "doe"} ) self.assertRedirects( resp, reverse("account_email_verification_sent"), fetch_redirect_response=False, ) self.assertEqual(len(mail.outbox), 1) assert mail.outbox[0].to == ["john@example.com"] def test_login_unverified_account_mandatory(self): """Tests login behavior when email verification is mandatory.""" user = get_user_model().objects.create(username="john") user.set_password("doe") user.save() EmailAddress.objects.create( user=user, email="user@example.com", primary=True, verified=False ) resp = self.client.post( reverse("account_login"), {"login": "john", "password": "doe"} ) self.assertRedirects(resp, reverse("account_email_verification_sent")) def test_login_inactive_account(self): """ Tests login behavior with inactive accounts. Inactive user accounts should be prevented from performing any actions, regardless of their verified state. """ # Inactive and verified user account user = get_user_model().objects.create(username="john", is_active=False) user.set_password("doe") user.save() EmailAddress.objects.create( user=user, email="john@example.com", primary=True, verified=True ) resp = self.client.post( reverse("account_login"), {"login": "john", "password": "doe"} ) self.assertRedirects(resp, reverse("account_inactive")) # Inactive and unverified user account user = get_user_model().objects.create(username="doe", is_active=False) user.set_password("john") user.save() EmailAddress.objects.create( user=user, email="user@example.com", primary=True, verified=False ) resp = self.client.post( reverse("account_login"), {"login": "doe", "password": "john"} ) self.assertRedirects(resp, reverse("account_inactive")) @override_settings(ACCOUNT_AUTHENTICATED_LOGIN_REDIRECTS=False) def test_account_authenticated_login_redirects_is_false(self): self._create_user_and_login() resp = self.client.get(reverse("account_login")) self.assertEqual(resp.status_code, HTTPStatus.OK) def test_login_password_forgotten_link_not_present(client, db): with patch("allauth.account.forms.reverse") as reverse_mock: reverse_mock.side_effect = NoReverseMatch form = LoginForm() assert form.fields["password"].help_text == "" def test_login_password_forgotten_link_present(client, db): form = LoginForm() assert ( form.fields["password"].help_text == 'Forgot your password?' ) def test_login_while_authenticated(settings, client, user_factory): settings.ACCOUNT_AUTHENTICATED_LOGIN_REDIRECTS = False user_factory(username="john", email="john@example.org", password="doe") user_factory(username="jane", email="jane@example.org", password="doe") redirect_url = settings.LOGIN_REDIRECT_URL resp = client.post(reverse("account_login"), {"login": "john", "password": "doe"}) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == redirect_url resp = client.post(reverse("account_login"), {"login": "jane", "password": "doe"}) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == redirect_url def test_login_page(client, db): resp = client.get(reverse("account_login")) assert resp.status_code == HTTPStatus.OK assertTemplateUsed(resp, "account/login.html") ================================================ FILE: tests/apps/account/test_login_by_code.py ================================================ from http import HTTPStatus from unittest.mock import ANY from django.urls import reverse import pytest from allauth import app_settings as allauth_settings from allauth.account.authentication import AUTHENTICATION_METHODS_SESSION_KEY from allauth.account.internal.stagekit import LOGIN_SESSION_KEY from allauth.account.models import EmailAddress from allauth.account.stages import LoginByCodeStage @pytest.fixture def get_expected_login_code(): def f(client): return client.session[LOGIN_SESSION_KEY]["state"]["stages"][ LoginByCodeStage.key ]["data"]["code"] return f @pytest.fixture def request_login_by_code(mailoutbox, get_expected_login_code): def f(client, email): resp = client.get(f"{reverse('account_request_login_code')}?next=/foo") assert resp.status_code == HTTPStatus.OK assert b'value="/foo"' in resp.content resp = client.post( reverse("account_request_login_code"), data={"email": email, "next": "/foo"} ) assert resp.status_code == HTTPStatus.FOUND assert ( resp["location"] == f"{reverse('account_confirm_login_code')}?next=%2Ffoo" ) assert len(mailoutbox) == 1 code = get_expected_login_code(client) assert len(code) == 9 assert code in mailoutbox[0].body return code return f def test_login_by_code(client, user, request_login_by_code): code = request_login_by_code(client, user.email) code_with_ws = f" {code[0:3]} {code[3:]}" resp = client.post( reverse("account_confirm_login_code"), data={"code": code_with_ws, "next": "/foo"}, ) assert resp.status_code == HTTPStatus.FOUND assert LOGIN_SESSION_KEY not in client.session assert resp["location"] == "/foo" assert client.session[AUTHENTICATION_METHODS_SESSION_KEY][-1] == { "method": "code", "email": user.email, "at": ANY, } def test_login_by_code_resend_limited( client, user, request_login_by_code, settings, get_expected_login_code ): settings.ACCOUNT_LOGIN_BY_CODE_SUPPORTS_RESEND = 2 code = request_login_by_code(client, user.email) for i in range(3): resp = client.post( reverse("account_confirm_login_code"), data={"action": "resend"} ) assert resp.status_code == ( # Form error if resend not possible HTTPStatus.OK if i == 2 # If resend allowed, the resend form is valid and results in a redirect. else HTTPStatus.FOUND ) new_code = get_expected_login_code(client) if i == 2: assert new_code == code else: assert new_code != code code = new_code def test_login_by_code_resend_uses_new_code( client, user, request_login_by_code, settings, get_expected_login_code ): settings.ACCOUNT_LOGIN_BY_CODE_SUPPORTS_RESEND = 2 code = request_login_by_code(client, user.email) resp = client.post(reverse("account_confirm_login_code"), data={"action": "resend"}) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_confirm_login_code") new_code = get_expected_login_code(client) assert new_code != code resp = client.post(reverse("account_confirm_login_code"), data={"code": new_code}) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == settings.LOGIN_REDIRECT_URL def test_login_by_code_max_attempts(client, user, request_login_by_code, settings): settings.ACCOUNT_LOGIN_BY_CODE_MAX_ATTEMPTS = 2 request_login_by_code(client, user.email) for i in range(3): resp = client.post( reverse("account_confirm_login_code"), data={"code": "wrong"} ) if i >= 1: assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_request_login_code") assert LOGIN_SESSION_KEY not in client.session else: assert resp.status_code == HTTPStatus.OK assert LOGIN_SESSION_KEY in client.session assert resp.context["form"].errors == {"code": ["Incorrect code."]} def test_login_by_code_unknown_user(mailoutbox, client, db): resp = client.post( reverse("account_request_login_code"), data={"email": "unknown@email.org"}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_confirm_login_code") resp = client.post(reverse("account_confirm_login_code"), data={"code": "123456"}) @pytest.mark.parametrize( "setting,code_required", [ (True, True), ({"password"}, True), ({"socialaccount"}, False), ], ) def test_login_by_code_required( client, settings, user_factory, password_factory, setting, code_required ): password = password_factory() user = user_factory(password=password, email_verified=False) email_address = EmailAddress.objects.get(email=user.email) assert not email_address.verified settings.ACCOUNT_LOGIN_BY_CODE_REQUIRED = setting resp = client.post( reverse("account_login"), data={"login": user.username, "password": password}, ) assert resp.status_code == HTTPStatus.FOUND if code_required: assert resp["location"] == reverse("account_confirm_login_code") code = client.session[LOGIN_SESSION_KEY]["state"]["stages"][ LoginByCodeStage.key ]["data"]["code"] resp = client.get( reverse("account_confirm_login_code"), data={"login": user.username, "password": password}, ) assert resp.status_code == HTTPStatus.OK resp = client.post(reverse("account_confirm_login_code"), data={"code": code}) email_address.refresh_from_db() assert email_address.verified assert resp["location"] == settings.LOGIN_REDIRECT_URL def test_login_by_code_redirect(client, user, request_login_by_code): request_login_by_code(client, user.email) resp = client.get(reverse("account_login")) assert resp["location"] == reverse("account_confirm_login_code") @pytest.mark.parametrize("action", ["", "trust"]) def test_trust_flow( client, user, user_password, settings_impacting_urls, action, settings, ): if not allauth_settings.MFA_ENABLED: return with settings_impacting_urls( ACCOUNT_LOGIN_BY_CODE_TRUST_ENABLED=True, ACCOUNT_LOGIN_BY_CODE_REQUIRED=True ): # Login resp = client.post( reverse("account_login"), {"login": user.username, "password": user_password}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_confirm_login_code") code = client.session[LOGIN_SESSION_KEY]["state"]["stages"][ LoginByCodeStage.key ]["data"]["code"] # Enter code resp = client.post(reverse("account_confirm_login_code"), data={"code": code}) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("mfa_trust") # Indicate trust resp = client.post( reverse("mfa_trust"), {"action": action}, ) assert resp["location"] == settings.LOGIN_REDIRECT_URL # Sign out resp = client.post( reverse("account_logout"), ) assert resp.status_code == HTTPStatus.FOUND # Sign in resp = client.post( reverse("account_login"), {"login": user.username, "password": user_password}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == ( settings.LOGIN_REDIRECT_URL if action == "trust" else reverse("account_confirm_login_code") ) def test_trust_while_already_register( client, user, user_password, settings_impacting_urls, settings, mailoutbox ): if not allauth_settings.MFA_ENABLED: return with settings_impacting_urls( ACCOUNT_SIGNUP_FIELDS=["email*", "password*"], ACCOUNT_LOGIN_BY_CODE_TRUST_ENABLED=True, ACCOUNT_LOGIN_BY_CODE_REQUIRED=True, ACCOUNT_PREVENT_ENUMERATION=True, ACCOUNT_EMAIL_VERIFICATION="mandatory", ): resp = client.post( reverse("account_signup"), {"email": user.email, "password": user_password}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_confirm_login_code") assert mailoutbox[0].subject == "[example.com] Account Already Exists" ================================================ FILE: tests/apps/account/test_logout.py ================================================ from django.contrib.auth import get_user_model from django.core import validators from django.test import TestCase from django.test.client import Client from django.test.utils import override_settings from django.urls import reverse from allauth.account import app_settings from allauth.account.signals import user_logged_out from tests.mocking import Mock test_username_validators = [ validators.RegexValidator(regex=r"^[a-c]+$", message="not abc") ] @override_settings( ACCOUNT_DEFAULT_HTTP_PROTOCOL="https", ACCOUNT_EMAIL_VERIFICATION=app_settings.EmailVerificationMethod.MANDATORY, ACCOUNT_LOGIN_METHODS={app_settings.LoginMethod.USERNAME}, ACCOUNT_SIGNUP_FORM_CLASS=None, ACCOUNT_EMAIL_SUBJECT_PREFIX=None, LOGIN_REDIRECT_URL="/accounts/profile/", ACCOUNT_SIGNUP_REDIRECT_URL="/accounts/welcome/", ACCOUNT_ADAPTER="allauth.account.adapter.DefaultAccountAdapter", ACCOUNT_USERNAME_REQUIRED=True, ) class LogoutTests(TestCase): @override_settings(ACCOUNT_LOGOUT_ON_GET=True) def test_logout_view_on_get(self): c, resp = self._logout_view("get") self.assertTemplateUsed(resp, "account/messages/logged_out.txt") @override_settings(ACCOUNT_LOGOUT_ON_GET=False) def test_logout_view_on_post(self): c, resp = self._logout_view("get") self.assertTemplateUsed( resp, f"account/logout.{app_settings.TEMPLATE_EXTENSION}" ) receiver_mock = Mock() user_logged_out.connect(receiver_mock) resp = c.post(reverse("account_logout")) self.assertTemplateUsed(resp, "account/messages/logged_out.txt") receiver_mock.assert_called_once_with( sender=get_user_model(), request=resp.wsgi_request, user=get_user_model().objects.get(username="john"), signal=user_logged_out, ) user_logged_out.disconnect(receiver_mock) def _logout_view(self, method): c = Client() user = get_user_model().objects.create(username="john", is_active=True) user.set_password("doe") user.save() c = Client() c.login(username="john", password="doe") return c, getattr(c, method)(reverse("account_logout")) ================================================ FILE: tests/apps/account/test_middleware.py ================================================ import django from django.http import HttpResponse from django.test.client import AsyncClient from django.urls import path, reverse import pytest from allauth.account.internal.decorators import login_not_required from allauth.core.exceptions import ImmediateHttpResponse @login_not_required def raise_immediate_http_response(request): response = HttpResponse(content="raised-response") raise ImmediateHttpResponse(response=response) urlpatterns = [path("raise", raise_immediate_http_response)] def test_immediate_http_response(settings, client): settings.ROOT_URLCONF = "tests.apps.account.test_middleware" resp = client.get("/raise") assert resp.content == b"raised-response" skip_django_lt_5 = pytest.mark.skipif( django.VERSION[0] < 5, reason="This test is allowed to fail on Django <5." ) @skip_django_lt_5 @pytest.mark.asyncio @pytest.mark.django_db(transaction=True) async def test_accounts_redirect_async_ctx(user, db): aclient = AsyncClient() await aclient.aforce_login(user) resp = await aclient.get("/accounts/") assert resp["location"] == reverse("account_email") ================================================ FILE: tests/apps/account/test_models.py ================================================ import uuid from django.contrib.auth.models import AbstractUser from django.db import models from allauth.account.models import EmailAddress class UUIDUser(AbstractUser): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) class Meta(AbstractUser.Meta): # type: ignore[name-defined] swappable = "AUTH_USER_MODEL" app_label = "dummy" def test_add_new_email(rf, user, settings): settings.ACCOUNT_CHANGE_EMAIL = True request = rf.get("/") assert EmailAddress.objects.filter(user=user).count() == 1 new_email = EmailAddress.objects.add_new_email( request, user, "new@email.org", send_verification=False ) assert not new_email.verified assert not new_email.primary assert EmailAddress.objects.filter(user=user).count() == 2 EmailAddress.objects.add_new_email( request, user, "new2@email.org", send_verification=False ) assert EmailAddress.objects.filter(user=user).count() == 2 assert not EmailAddress.objects.filter(pk=new_email.pk).exists() assert EmailAddress.objects.filter(email="new2@email.org", verified=False).exists() ================================================ FILE: tests/apps/account/test_phone.py ================================================ from http import HTTPStatus from unittest.mock import ANY from django.conf import settings from django.test import Client from django.urls import reverse import pytest from pytest_django.asserts import assertTemplateUsed from allauth.account.adapter import get_adapter from allauth.account.authentication import AUTHENTICATION_METHODS_SESSION_KEY @pytest.fixture def phone_only_settings(settings_impacting_urls): with settings_impacting_urls( ACCOUNT_LOGIN_METHODS=("phone",), ACCOUNT_SIGNUP_FIELDS=["phone*"] ): yield def test_signup(db, client, phone, sms_outbox, phone_only_settings): assert len(sms_outbox) == 0 resp = client.post(reverse("account_signup"), data={"phone": phone}) assert resp.status_code == HTTPStatus.FOUND assert len(sms_outbox) == 1 assert resp["location"] == reverse("account_verify_phone") resp = client.get(resp["location"]) assert resp.status_code == HTTPStatus.OK resp = client.post( reverse("account_verify_phone"), data={"code": sms_outbox[-1]["code"]} ) assert resp.status_code == HTTPStatus.FOUND adapter = get_adapter() user = adapter.get_user_by_phone(phone) phone2, phone_verified = adapter.get_phone(user) assert phone_verified assert phone == phone2 assert not user.check_password("") def test_signup_invalid_attempts(db, client, phone, sms_outbox, phone_only_settings): assert len(sms_outbox) == 0 resp = client.post(reverse("account_signup"), data={"phone": phone}) assert resp.status_code == HTTPStatus.FOUND adapter = get_adapter() user = adapter.get_user_by_phone(phone) _, phone_verified = adapter.get_phone(user) assert not phone_verified assert len(sms_outbox) == 1 assert resp["location"] == reverse("account_verify_phone") resp = client.get(resp["location"]) assert resp.status_code == HTTPStatus.OK for i in range(3): resp = client.post(reverse("account_verify_phone"), data={"code": "wrong"}) assert resp.status_code == (HTTPStatus.OK if i < 2 else HTTPStatus.FOUND) def test_login_sends_code( user_with_phone, client, phone_only_settings, phone, sms_outbox ): resp = client.post(reverse("account_login"), data={"login": phone}) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_confirm_login_code") assert len(sms_outbox) == 1 def test_login_with_verified_phone_and_password( client, settings_impacting_urls, phone, user_with_phone, user_password ): with settings_impacting_urls( ACCOUNT_SIGNUP_FIELDS=["phone*", "password1*"], ACCOUNT_LOGIN_METHODS=["phone"], ): resp = client.post( reverse("account_login"), data={"login": phone, "password": user_password} ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == settings.LOGIN_REDIRECT_URL def test_login_with_unverified_phone_and_password( client, settings_impacting_urls, phone, password_factory, user_factory, sms_outbox ): with settings_impacting_urls( ACCOUNT_SIGNUP_FIELDS=["phone*", "password1*"], ACCOUNT_LOGIN_METHODS=["phone"], ): password = password_factory() user = user_factory(phone=phone, password=password, phone_verified=False) resp = client.post( reverse("account_login"), data={"login": phone, "password": password} ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_verify_phone") code = sms_outbox[-1]["code"] resp = client.post(reverse("account_verify_phone"), data={"code": code}) assert resp["location"] == settings.LOGIN_REDIRECT_URL phone_verified = get_adapter().get_phone(user) assert phone_verified == (phone, True) def test_change_phone( auth_client, user, phone_only_settings, phone_factory, sms_outbox ): new_phone = phone_factory() resp = auth_client.get(reverse("account_change_phone")) assert resp.status_code == HTTPStatus.OK assertTemplateUsed(resp, "account/phone_change.html") resp = auth_client.post(reverse("account_change_phone"), {"phone": new_phone}) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_verify_phone") code = sms_outbox[-1]["code"] resp = auth_client.get(reverse("account_verify_phone")) assert resp.status_code == HTTPStatus.OK assertTemplateUsed(resp, "account/confirm_phone_verification_code.html") resp = auth_client.post(reverse("account_verify_phone"), {"code": code}) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_change_phone") phone_verified = get_adapter().get_phone(user) assert phone_verified == (new_phone, True) resp = auth_client.get(reverse("account_verify_phone")) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_change_phone") def test_change_phone_to_already_existing( auth_client, user, phone_only_settings, phone_factory, sms_outbox, user_factory ): other_phone = phone_factory() user_factory(phone=other_phone) resp = auth_client.post(reverse("account_change_phone"), {"phone": other_phone}) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_verify_phone") code = sms_outbox[-1]["code"] resp = auth_client.get(reverse("account_verify_phone")) assert resp.status_code == HTTPStatus.OK assertTemplateUsed(resp, "account/confirm_phone_verification_code.html") resp = auth_client.post(reverse("account_verify_phone"), {"code": code}) assert resp.status_code == HTTPStatus.OK assert resp.context["form"].errors == { "code": ["A user is already registered with this phone number."] } def test_login_by_code_enumeration_prevention( db, phone_only_settings, client, phone_factory, sms_outbox ): resp = client.post( reverse("account_request_login_code"), data={"phone": phone_factory()} ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_confirm_login_code") assert "code" not in sms_outbox[-1] assert "user_id" not in sms_outbox[-1] def test_reauthentication( settings, auth_client, user_with_phone, phone_factory, settings_impacting_urls, user_password, ): with settings_impacting_urls( ACCOUNT_REAUTHENTICATION_REQUIRED=True, ACCOUNT_LOGIN_METHODS=("phone",), ACCOUNT_SIGNUP_FIELDS=["phone*", "password1*"], ): new_phone = phone_factory() resp = auth_client.post( reverse("account_change_phone"), {"phone": new_phone}, ) assert resp["location"].startswith(reverse("account_reauthenticate")) resp = auth_client.get(reverse("account_reauthenticate")) assertTemplateUsed(resp, "account/reauthenticate.html") resp = auth_client.post( reverse("account_reauthenticate"), data={"password": user_password} ) assert resp.status_code == HTTPStatus.FOUND methods = auth_client.session[AUTHENTICATION_METHODS_SESSION_KEY] assert methods[-1] == {"method": "password", "at": ANY, "reauthenticated": True} resp = auth_client.post( reverse("account_change_phone"), {"phone": new_phone}, ) assert resp["location"].startswith(reverse("account_verify_phone")) def test_signup_conflict(db, phone, sms_outbox, phone_only_settings): assert len(sms_outbox) == 0 client = Client() resp = client.post(reverse("account_signup"), data={"phone": phone}) assert resp.status_code == HTTPStatus.FOUND assert len(sms_outbox) == 1 client = Client() resp = client.post(reverse("account_signup"), data={"phone": phone}) assert resp.status_code == HTTPStatus.FOUND assert len(sms_outbox) == 2 assert sms_outbox == [ {"code": ANY, "phone": phone, "user_id": 1}, {"phone": phone, "reason": "exists"}, ] def test_change_to_already_existing_phone( client, db, settings, phone_factory, sms_outbox, messagesoutbox, user_with_phone, phone_only_settings, ): settings.ACCOUNT_PHONE_VERIFICATION_SUPPORTS_RESEND = True settings.ACCOUNT_PHONE_VERIFICATION_SUPPORTS_CHANGE = True settings.ACCOUNT_PREVENT_ENUMERATION = True new_phone = phone_factory() resp = client.post( reverse("account_signup"), {"phone": new_phone}, ) # A user signed up. new_user = get_adapter().get_user_by_phone(new_phone) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_verify_phone") assert len(sms_outbox) == 1 assert sms_outbox[0] == {"code": ANY, "phone": new_phone, "user_id": new_user.pk} # assert len(messagesoutbox) == 1 # assert ( # messagesoutbox[-1]["message_template"] # == "account/messages/email_confirmation_sent.txt" # ) # Change to a conflicting phone number existing_phone_verified = get_adapter().get_phone(user_with_phone) resp = client.post( reverse("account_verify_phone"), {"action": "change", "phone": existing_phone_verified[0]}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_verify_phone") assert len(sms_outbox) == 2 assert sms_outbox[1] == {"phone": existing_phone_verified[0], "reason": "exists"} # assert len(messagesoutbox) == 2 # assert ( # messagesoutbox[-1]["message_template"] # == "account/messages/email_confirmation_sent.txt" # ) assert get_adapter().get_phone(new_user)[0] == new_phone # Change back to new phone new_phone2 = phone_factory() resp = client.post( reverse("account_verify_phone"), {"action": "change", "phone": new_phone2}, ) assert len(sms_outbox) == 3 assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_verify_phone") assert sms_outbox[2] == {"code": ANY, "phone": new_phone2, "user_id": ANY} # assert len(messagesoutbox) == 3 # assert ( # messagesoutbox[-1]["message_template"] # == "account/messages/email_confirmation_sent.txt" # ) assert get_adapter().get_phone(new_user)[0] == new_phone2 resp = client.post( reverse("account_verify_phone"), data={"code": sms_outbox[-1]["code"]}, ) assert resp.status_code == HTTPStatus.FOUND assert get_adapter().get_phone(new_user) == (new_phone2, True) def test_signup_invalid_phone(db, client, sms_outbox, phone_only_settings): assert len(sms_outbox) == 0 resp = client.post(reverse("account_signup"), data={"phone": "notaphone"}) assert resp.status_code == HTTPStatus.OK assert resp.context["form"].errors == { "phone": ["Enter a phone number including country code (e.g. +1 for the US)."] } ================================================ FILE: tests/apps/account/test_ratelimit.py ================================================ from http import HTTPStatus from django.urls import reverse def test_case_insensitive_password_reset(settings, enable_cache, user_factory, client): settings.ACCOUNT_RATE_LIMITS = {"reset_password": "1/m"} user_factory(email="a@b.com") resp = client.post(reverse("account_reset_password"), data={"email": "a@b.com"}) assert resp.status_code == HTTPStatus.FOUND resp = client.post(reverse("account_reset_password"), data={"email": "A@B.COM"}) assert resp.status_code == HTTPStatus.TOO_MANY_REQUESTS ================================================ FILE: tests/apps/account/test_reauthentication.py ================================================ from http import HTTPStatus from unittest.mock import ANY from django.urls import reverse import pytest from pytest_django.asserts import assertTemplateUsed from allauth import app_settings as allauth_settings from allauth.account.adapter import get_adapter from allauth.account.authentication import AUTHENTICATION_METHODS_SESSION_KEY @pytest.mark.parametrize( "with_totp,with_password,expected_method_urlnames", [ (False, True, ["account_reauthenticate"]), (True, True, ["account_reauthenticate", "mfa_reauthenticate"]), (True, False, ["mfa_reauthenticate"]), ], ) def test_user_with_mfa_only( user_factory, with_totp, with_password, expected_method_urlnames, client ): if not allauth_settings.MFA_ENABLED and with_totp: return user = user_factory(with_totp=with_totp, password=None if with_password else "!") assert user.has_usable_password() == with_password client.force_login(user) methods = get_adapter().get_reauthentication_methods(user) assert len(methods) == len(expected_method_urlnames) assert {m["url"] for m in methods} == set(map(reverse, expected_method_urlnames)) for urlname in ["account_reauthenticate", "mfa_reauthenticate"]: if urlname == "mfa_reauthenticate" and not allauth_settings.MFA_ENABLED: continue resp = client.get(f"{reverse(urlname)}?next=/foo") if urlname in expected_method_urlnames: assert resp.status_code == HTTPStatus.OK else: assert resp.status_code == HTTPStatus.FOUND assert "next=%2Ffoo" in resp["location"] def test_reauthentication(settings, auth_client, user_password): settings.ACCOUNT_REAUTHENTICATION_REQUIRED = True resp = auth_client.post( reverse("account_email"), {"action_add": "", "email": "john3@example.org"}, ) assert resp["location"].startswith(reverse("account_reauthenticate")) resp = auth_client.get(reverse("account_reauthenticate")) assertTemplateUsed(resp, "account/reauthenticate.html") resp = auth_client.post( reverse("account_reauthenticate"), data={"password": user_password} ) assert resp.status_code == HTTPStatus.FOUND resp = auth_client.post( reverse("account_email"), {"action_add": "", "email": "john3@example.org"}, ) assert resp["location"].startswith(reverse("account_email")) methods = auth_client.session[AUTHENTICATION_METHODS_SESSION_KEY] assert methods[-1] == {"method": "password", "at": ANY, "reauthenticated": True} ================================================ FILE: tests/apps/account/test_reset_password.py ================================================ import json from http import HTTPStatus from django.contrib.auth import get_user_model from django.contrib.auth.models import AnonymousUser from django.core import mail from django.test import TestCase from django.test.utils import override_settings from django.urls import reverse, reverse_lazy from django.utils.http import urlencode import pytest from pytest_django.asserts import assertRedirects, assertTemplateUsed from allauth.account import app_settings from allauth.account.forms import ResetPasswordForm, default_token_generator from allauth.account.models import EmailAddress from allauth.account.utils import user_pk_to_url_str @pytest.fixture def password_reset_url(): def f(user): temp_key = default_token_generator.make_token(user) uid = user_pk_to_url_str(user) return reverse( "account_reset_password_from_key", kwargs={"uidb36": uid, "key": temp_key} ) return f @pytest.mark.django_db def test_reset_password_unknown_account(client, settings): settings.ACCOUNT_PREVENT_ENUMERATION = True resp = client.post( reverse("account_reset_password"), data={"email": "unknown@example.org"}, follow=True, ) assert len(mail.outbox) == 1 assert mail.outbox[0].to == ["unknown@example.org"] assert resp.redirect_chain == [ (reverse("account_reset_password_done"), HTTPStatus.FOUND) ] @pytest.mark.django_db def test_reset_password_unknown_account_disabled(client, settings): settings.ACCOUNT_PREVENT_ENUMERATION = True settings.ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS = False client.post( reverse("account_reset_password"), data={"email": "unknown@example.org"}, ) assert len(mail.outbox) == 0 @pytest.mark.parametrize( "query,expected_location", [("", reverse_lazy("account_reset_password_done")), ("?next=/foo", "/foo")], ) def test_reset_password_next_url(client, user, query, expected_location): resp = client.post( reverse("account_reset_password") + query, data={"email": user.email}, ) assert resp["location"] == expected_location @override_settings( ACCOUNT_PREVENT_ENUMERATION=False, ACCOUNT_DEFAULT_HTTP_PROTOCOL="https", ACCOUNT_EMAIL_VERIFICATION=app_settings.EmailVerificationMethod.MANDATORY, ACCOUNT_LOGIN_METHODS={app_settings.AuthenticationMethod.USERNAME}, ACCOUNT_SIGNUP_FORM_CLASS=None, ACCOUNT_EMAIL_SUBJECT_PREFIX=None, LOGIN_REDIRECT_URL="/accounts/profile/", ACCOUNT_SIGNUP_REDIRECT_URL="/accounts/welcome/", ACCOUNT_ADAPTER="allauth.account.adapter.DefaultAccountAdapter", ACCOUNT_USERNAME_REQUIRED=True, ACCOUNT_EMAIL_NOTIFICATIONS=True, ) class ResetPasswordTests(TestCase): def test_user_email_not_sent_inactive_user(self): User = get_user_model() User.objects.create_user( "mike123", "mike@ixample.org", "test123", is_active=False ) data = {"email": "mike@ixample.org"} form = ResetPasswordForm(data) self.assertFalse(form.is_valid()) def test_password_reset_get(self): resp = self.client.get(reverse("account_reset_password")) self.assertTemplateUsed(resp, "account/password_reset.html") def test_set_password_not_allowed(self): user = self._create_user_and_login(True) pwd = "!*123i1uwn12W23" self.assertFalse(user.check_password(pwd)) resp = self.client.post( reverse("account_set_password"), data={"password1": pwd, "password2": pwd}, ) user.refresh_from_db() self.assertFalse(user.check_password(pwd)) self.assertTrue(user.has_usable_password()) self.assertEqual(resp.status_code, HTTPStatus.FOUND) def test_password_forgotten_username_hint(self): user = self._request_new_password() body = mail.outbox[0].body assert user.username in body @override_settings(ACCOUNT_LOGIN_METHODS={app_settings.AuthenticationMethod.EMAIL}) def test_password_forgotten_no_username_hint(self): user = self._request_new_password() body = mail.outbox[0].body assert user.username not in body def _request_new_password(self): user = get_user_model().objects.create( username="john", email="john@example.org", is_active=True ) user.set_password("doe") user.save() self.client.post( reverse("account_reset_password"), data={"email": "john@example.org"}, ) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].to, ["john@example.org"]) return user def test_password_reset_flow_with_empty_session(self): """ Test the password reset flow when the session is empty: requesting a new password, receiving the reset link via email, following the link, getting redirected to the new link (without the token) Copying the link and using it in a DIFFERENT client (Browser/Device). """ # Request new password self._request_new_password() body = mail.outbox[0].body self.assertGreater(body.find("https://"), 0) # Extract URL for `password_reset_from_key` view url = body[body.find("/accounts/password/reset/") :].split()[0] resp = self.client.get(url) reset_pass_url = resp.url # Accessing the url via a different session resp = self.client_class().get(reset_pass_url) # We should receive the token_fail context_data self.assertTemplateUsed( resp, f"account/password_reset_from_key.{app_settings.TEMPLATE_EXTENSION}", ) self.assertTrue(resp.context_data["token_fail"]) @override_settings(ACCOUNT_LOGIN_METHODS={app_settings.AuthenticationMethod.EMAIL}) def test_password_reset_flow_with_another_user_logged_in(self): """ Tests the password reset flow: if User B requested a password reset earlier and now User A is logged in, User B now clicks on the link, ensure User A is logged out before continuing. """ # Request new password self._request_new_password() body = mail.outbox[0].body self.assertGreater(body.find("https://"), 0) user2 = self._create_user(username="john2", email="john2@example.com") EmailAddress.objects.create( user=user2, email=user2.email, primary=True, verified=True ) resp = self.client.post( reverse("account_login"), { "login": user2.email, "password": "doe", }, ) self.assertEqual(user2, resp.context["user"]) # Extract URL for `password_reset_from_key` view and access it url = body[body.find("/accounts/password/reset/") :].split()[0] resp = self.client.get(url) # Follow the redirect the actual password reset page with the key # hidden. url = resp.url resp = self.client.get(url) self.assertTemplateUsed( resp, f"account/password_reset_from_key.{app_settings.TEMPLATE_EXTENSION}" ) self.assertFalse("token_fail" in resp.context_data) # Reset the password resp = self.client.post( url, {"password1": "newpass123", "password2": "newpass123"}, follow=True ) self.assertRedirects(resp, reverse("account_reset_password_from_key_done")) self.assertNotEqual(user2, resp.context["user"]) self.assertEqual(AnonymousUser(), resp.context["user"]) def test_password_reset_flow_with_email_changed(self): """ Test that the password reset token is invalidated if the user email address was changed. """ user = self._request_new_password() body = mail.outbox[0].body self.assertGreater(body.find("https://"), 0) EmailAddress.objects.create(user=user, email="other@email.org") # Extract URL for `password_reset_from_key` view url = body[body.find("/accounts/password/reset/") :].split()[0] resp = self.client.get(url) self.assertTemplateUsed( resp, f"account/password_reset_from_key.{app_settings.TEMPLATE_EXTENSION}", ) self.assertTrue("token_fail" in resp.context_data) @override_settings(ACCOUNT_LOGIN_ON_PASSWORD_RESET=True) def test_password_reset_ACCOUNT_LOGIN_ON_PASSWORD_RESET(self): user = self._request_new_password() body = mail.outbox[0].body url = body[body.find("/accounts/password/reset/") :].split()[0] resp = self.client.get(url) # Follow the redirect the actual password reset page with the key # hidden. resp = self.client.post( resp.url, {"password1": "newpass123", "password2": "newpass123"} ) self.assertTrue(user.is_authenticated) # EmailVerificationMethod.MANDATORY sends us to the confirm-email page self.assertRedirects(resp, "/accounts/confirm-email/") def _create_user(self, username="john", password="doe", **kwargs): user = get_user_model().objects.create( username=username, is_active=True, **kwargs ) if password: user.set_password(password) else: user.set_unusable_password() user.save() return user def _create_user_and_login(self, usable_password=True): password = "doe" if usable_password else False user = self._create_user(password=password) self.client.force_login(user) return user def test_password_reset_flow(client, user, mailoutbox, settings): """ Tests the password reset flow: requesting a new password, receiving the reset link via email and finally resetting the password to a new value. """ settings.ACCOUNT_EMAIL_NOTIFICATIONS = True # Request new password client.post( reverse("account_reset_password"), data={"email": user.email}, ) assert len(mail.outbox) == 1 assert mailoutbox[0].to == [user.email] body = mailoutbox[0].body assert body.find("http://") > 0 # Extract URL for `password_reset_from_key` view and access it url = body[body.find("/accounts/password/reset/") :].split()[0] resp = client.get(url) # Follow the redirect the actual password reset page with the key # hidden. url = resp.url resp = client.get(url) assertTemplateUsed( resp, f"account/password_reset_from_key.{app_settings.TEMPLATE_EXTENSION}", ) assert "token_fail" not in resp.context_data # Reset the password resp = client.post(url, {"password1": "newpass123", "password2": "newpass123"}) assertRedirects(resp, reverse("account_reset_password_from_key_done")) assert "Your password has been reset" in mailoutbox[-1].body # Check the new password is in effect user = get_user_model().objects.get(pk=user.pk) assert user.check_password("newpass123") # Trying to reset the password against the same URL (or any other # invalid/obsolete URL) returns a bad token response resp = client.post(url, {"password1": "newpass123", "password2": "newpass123"}) assertTemplateUsed( resp, f"account/password_reset_from_key.{app_settings.TEMPLATE_EXTENSION}", ) assert resp.context_data["token_fail"] # Same should happen when accessing the page directly response = client.get(url) assertTemplateUsed( response, f"account/password_reset_from_key.{app_settings.TEMPLATE_EXTENSION}", ) assert response.context_data["token_fail"] # When in XHR views, it should respond with a 400 bad request # code, and the response body should contain the JSON-encoded # error from the adapter response = client.post( url, {"password1": "newpass123", "password2": "newpass123"}, HTTP_X_REQUESTED_WITH="XMLHttpRequest", ) assert response.status_code == HTTPStatus.BAD_REQUEST data = json.loads(response.content.decode("utf8")) assert "invalid" in data["form"]["errors"][0] @pytest.mark.parametrize( "next_url,expected_location", [(None, reverse_lazy("account_reset_password_from_key_done")), ("/foo", "/foo")], ) def test_reset_password_from_key_next_url( user, client, password_factory, next_url, expected_location, password_reset_url ): url = password_reset_url(user) query = "" if next_url: query = f"?{urlencode({'next': next_url})}" resp = client.get(url + query) assert resp.status_code == HTTPStatus.FOUND assert ( resp["location"] == reverse( "account_reset_password_from_key", kwargs={"uidb36": user_pk_to_url_str(user), "key": "set-password"}, ) + query ) password = password_factory() data = {"password1": password, "password2": password} if next_url: data["next"] = next_url resp = client.post(resp["location"], data) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == expected_location ================================================ FILE: tests/apps/account/test_reset_password_by_code.py ================================================ from http import HTTPStatus from django.urls import reverse import pytest from allauth.account.models import EmailAddress @pytest.fixture(autouse=True) def prbc_settings(settings_impacting_urls): with settings_impacting_urls(ACCOUNT_PASSWORD_RESET_BY_CODE_ENABLED=True): yield @pytest.mark.parametrize("login_on_password_reset", [False, True]) def test_flow( user, client, mailoutbox, get_last_password_reset_code, password_factory, login_on_password_reset, settings, ): settings.ACCOUNT_LOGIN_ON_PASSWORD_RESET = login_on_password_reset new_password = password_factory() assert not user.check_password(new_password) resp = client.post(reverse("account_reset_password"), {"email": user.email}) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_confirm_password_reset_code") resp = client.post( reverse("account_confirm_password_reset_code"), {"code": get_last_password_reset_code(client, mailoutbox)}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_complete_password_reset") resp = client.post( reverse("account_complete_password_reset"), {"password1": new_password, "password2": new_password}, ) assert resp.status_code == HTTPStatus.FOUND assert ( resp["location"] == settings.LOGIN_REDIRECT_URL if login_on_password_reset else reverse("account_password_reset_completed") ) user.refresh_from_db() assert user.check_password(new_password) def test_prevent_enumeration(db, client, mailoutbox): resp = client.post( reverse("account_reset_password"), {"email": "unknown@account.org"} ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_confirm_password_reset_code") assert mailoutbox[0].subject == "[example.com] Unknown Account" resp = client.post( reverse("account_confirm_password_reset_code"), {"code": "none?"}, ) assert resp.status_code == HTTPStatus.OK def test_indirect_email_verification( db, client, mailoutbox, user_factory, get_last_password_reset_code ): user = user_factory(email_verified=False) address = EmailAddress.objects.get(user=user, email=user.email, verified=False) resp = client.post(reverse("account_reset_password"), {"email": address.email}) assert resp.status_code == HTTPStatus.FOUND resp = client.post( reverse("account_confirm_password_reset_code"), {"code": get_last_password_reset_code(client, mailoutbox)}, ) assert resp.status_code == HTTPStatus.FOUND address.refresh_from_db() assert address.verified ================================================ FILE: tests/apps/account/test_security.py ================================================ from allauth.account.forms import ResetPasswordForm def test_user_email_unicode_collision(settings, rf, user_factory, mailoutbox): settings.ACCOUNT_PREVENT_ENUMERATION = False user_factory(username="mike123", email="mike@example.org") user_factory(username="mike456", email="mıke@example.org") data = {"email": "mıke@example.org"} form = ResetPasswordForm(data) assert form.is_valid() form.save(rf.get("/")) assert len(mailoutbox) == 1 assert mailoutbox[0].to == ["mıke@example.org"] def test_user_email_domain_unicode_collision(settings, rf, user_factory, mailoutbox): settings.ACCOUNT_PREVENT_ENUMERATION = False user_factory(username="mike123", email="mike@ixample.org") user_factory(username="mike456", email="mike@ıxample.org") data = {"email": "mike@ıxample.org"} form = ResetPasswordForm(data) assert form.is_valid() form.save(rf.get("/")) assert len(mailoutbox) == 1 assert mailoutbox[0].to == ["mike@ıxample.org"] def test_user_email_unicode_collision_nonexistent(settings, user_factory): settings.ACCOUNT_PREVENT_ENUMERATION = False user_factory(username="mike123", email="mike@example.org") data = {"email": "mıke@example.org"} form = ResetPasswordForm(data) assert not form.is_valid() def test_user_email_domain_unicode_collision_nonexistent(settings, user_factory): settings.ACCOUNT_PREVENT_ENUMERATION = False user_factory(username="mike123", email="mike@ixample.org") data = {"email": "mike@ıxample.org"} form = ResetPasswordForm(data) assert not form.is_valid() ================================================ FILE: tests/apps/account/test_signup.py ================================================ from http import HTTPStatus from django import forms from django.contrib.auth import get_user_model from django.contrib.auth.models import AnonymousUser from django.contrib.messages.middleware import MessageMiddleware from django.contrib.sessions.middleware import SessionMiddleware from django.core import mail from django.test import TestCase from django.test.client import Client, RequestFactory from django.test.utils import override_settings from django.urls import reverse import pytest from pytest_django.asserts import assertTemplateUsed from allauth.account import app_settings from allauth.account.adapter import get_adapter from allauth.account.forms import BaseSignupForm, SignupForm from allauth.account.models import EmailAddress from allauth.core import context from allauth.utils import get_username_max_length class CustomSignupFormTests(TestCase): @override_settings( ACCOUNT_SIGNUP_EMAIL_ENTER_TWICE=True, ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE=True, ) def test_custom_form_field_order(self): expected_field_order = [ "email", "email2", "password1", "password2", "username", "last_name", "first_name", ] class TestSignupForm(forms.Form): first_name = forms.CharField(max_length=30) last_name = forms.CharField(max_length=30) field_order = expected_field_order class CustomSignupForm(SignupForm, TestSignupForm): # ACCOUNT_SIGNUP_FORM_CLASS is only abided by when the # BaseSignupForm definition is loaded the first time on Django # startup. @override_settings() has therefore no effect. pass form = CustomSignupForm() self.assertEqual(list(form.fields.keys()), expected_field_order) def test_user_class_attribute(self): from django.contrib.auth import get_user_model from django.db.models.query_utils import DeferredAttribute class CustomSignupForm(SignupForm): # ACCOUNT_SIGNUP_FORM_CLASS is only abided by when the # BaseSignupForm definition is loaded the first time on Django # startup. @override_settings() has therefore no effect. pass User = get_user_model() data = { "username": "username", "email": "user@example.com", "password1": "very-secret", "password2": "very-secret", } form = CustomSignupForm(data, email_required=True) assert isinstance(User.username, DeferredAttribute) form.is_valid() assert isinstance(User.username, DeferredAttribute) class BaseSignupFormTests(TestCase): @override_settings( ACCOUNT_USERNAME_REQUIRED=True, ACCOUNT_USERNAME_BLACKLIST=["username"] ) def test_username_in_blacklist(self): data = { "username": "username", "email": "user@example.com", } form = BaseSignupForm(data, email_required=True) self.assertFalse(form.is_valid()) @override_settings( ACCOUNT_USERNAME_REQUIRED=True, ACCOUNT_USERNAME_BLACKLIST=["username"] ) def test_username_not_in_blacklist(self): data = { "username": "theusername", "email": "user@example.com", } form = BaseSignupForm(data, email_required=True) self.assertTrue(form.is_valid()) @override_settings(ACCOUNT_USERNAME_REQUIRED=True) def test_username_maxlength(self): data = { "username": "username", "email": "user@example.com", } form = BaseSignupForm(data, email_required=True) max_length = get_username_max_length() field = form.fields["username"] self.assertEqual(field.max_length, max_length) widget = field.widget self.assertEqual(widget.attrs.get("maxlength"), str(max_length)) def test_signup_email_verification(settings, db): settings.ACCOUNT_USERNAME_REQUIRED = True settings.ACCOUNT_SIGNUP_EMAIL_ENTER_TWICE = True data = { "username": "username", "email": "user@example.com", } form = BaseSignupForm(data, email_required=True) assert not form.is_valid() data = { "username": "username", "email": "user@example.com", "email2": "USER@example.COM", } form = BaseSignupForm(data, email_required=True) assert form.is_valid() data["email2"] = "anotheruser@example.com" form = BaseSignupForm(data, email_required=True) assert not form.is_valid() @override_settings( ACCOUNT_DEFAULT_HTTP_PROTOCOL="https", ACCOUNT_EMAIL_VERIFICATION=app_settings.EmailVerificationMethod.MANDATORY, ACCOUNT_LOGIN_METHODS={app_settings.LoginMethod.USERNAME}, ACCOUNT_SIGNUP_FORM_CLASS=None, ACCOUNT_EMAIL_SUBJECT_PREFIX=None, LOGIN_REDIRECT_URL="/accounts/profile/", ACCOUNT_SIGNUP_REDIRECT_URL="/accounts/welcome/", ACCOUNT_ADAPTER="allauth.account.adapter.DefaultAccountAdapter", ACCOUNT_USERNAME_REQUIRED=True, ) class SignupTests(TestCase): def test_signup_same_email_verified_externally(self): user = self._test_signup_email_verified_externally( "john@example.com", "john@example.com" ) self.assertEqual(EmailAddress.objects.filter(user=user).count(), 1) EmailAddress.objects.get( verified=True, email="john@example.com", user=user, primary=True ) def test_signup_other_email_verified_externally(self): """ John is invited on john@example.org, but signs up via john@example.com. Email verification is by-passed, their home email address is used as a secondary. """ user = self._test_signup_email_verified_externally( "john@example.com", "john@example.org" ) self.assertEqual(EmailAddress.objects.filter(user=user).count(), 2) EmailAddress.objects.get( verified=False, email="john@example.com", user=user, primary=False ) EmailAddress.objects.get( verified=True, email="john@example.org", user=user, primary=True ) def _test_signup_email_verified_externally(self, signup_email, verified_email): username = "johndoe" request = RequestFactory().post( reverse("account_signup"), { "username": username, "email": signup_email, "password1": "johndoe", "password2": "johndoe", }, ) # Fake stash_verified_email SessionMiddleware(lambda request: None).process_request(request) MessageMiddleware(lambda request: None).process_request(request) request.user = AnonymousUser() request.session["account_verified_email"] = verified_email from allauth.account.views import signup with context.request_context(request): resp = signup(request) self.assertEqual(resp.status_code, HTTPStatus.FOUND) self.assertEqual( resp["location"], get_adapter().get_signup_redirect_url(request) ) self.assertEqual(len(mail.outbox), 0) return get_user_model().objects.get(username=username) @override_settings( ACCOUNT_USERNAME_REQUIRED=True, ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE=True, ) def test_signup_password_twice_form_error(self): resp = self.client.post( reverse("account_signup"), data={ "username": "johndoe", "email": "john@example.org", "password1": "johndoe", "password2": "janedoe", }, ) self.assertFormError( resp.context["form"], "password2", "You must type the same password each time.", ) @override_settings( ACCOUNT_USERNAME_REQUIRED=True, ACCOUNT_SIGNUP_EMAIL_ENTER_TWICE=True ) def test_signup_email_twice(self): request = RequestFactory().post( reverse("account_signup"), { "username": "johndoe", "email": "john@example.org", "email2": "john@example.org", "password1": "johndoe", "password2": "johndoe", }, ) SessionMiddleware(lambda request: None).process_request(request) MessageMiddleware(lambda request: None).process_request(request) request.user = AnonymousUser() from allauth.account.views import signup with context.request_context(request): signup(request) user = get_user_model().objects.get(username="johndoe") self.assertEqual(user.email, "john@example.org") @override_settings( AUTH_PASSWORD_VALIDATORS=[ { "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", "OPTIONS": { "min_length": 9, }, } ] ) def test_django_password_validation(self): resp = self.client.post( reverse("account_signup"), { "username": "johndoe", "email": "john@example.com", "password1": "johndoe", "password2": "johndoe", }, ) self.assertFormError(resp.context["form"], None, []) self.assertFormError( resp.context["form"], "password1", ["This password is too short. It must contain at least 9 characters."], ) def test_prevent_enumeration_with_mandatory_verification( settings, user_factory, email_factory ): settings.ACCOUNT_PREVENT_ENUMERATION = True settings.ACCOUNT_LOGIN_METHODS = {app_settings.LoginMethod.EMAIL} settings.ACCOUNT_EMAIL_VERIFICATION = app_settings.EmailVerificationMethod.MANDATORY user = user_factory(username="john", email="john@example.org", password="doe") c = Client() resp = c.post( reverse("account_signup"), { "username": "johndoe", "email": email_factory(email=user.email, mixed_case=True), "password1": "johndoe", "password2": "johndoe", }, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_email_verification_sent") assertTemplateUsed(resp, "account/email/account_already_exists_message.txt") assertTemplateUsed(resp, "account/messages/email_confirmation_sent.txt") assert EmailAddress.objects.filter(email="john@example.org").count() == 1 def test_prevent_enumeration_off(settings, user_factory, email_factory): settings.ACCOUNT_PREVENT_ENUMERATION = False settings.ACCOUNT_LOGIN_METHODS = {app_settings.LoginMethod.EMAIL} settings.ACCOUNT_EMAIL_VERIFICATION = app_settings.EmailVerificationMethod.MANDATORY user = user_factory(username="john", email="john@example.org", password="doe") c = Client() resp = c.post( reverse("account_signup"), { "username": "johndoe", "email": email_factory(email=user.email, mixed_case=True), "password1": "johndoe", "password2": "johndoe", }, ) assert resp.status_code == HTTPStatus.OK assert resp.context["form"].errors == { "email": ["A user is already registered with this email address."] } def test_prevent_enumeration_strictly(settings, user_factory, email_factory): settings.ACCOUNT_PREVENT_ENUMERATION = "strict" settings.ACCOUNT_LOGIN_METHODS = {app_settings.LoginMethod.EMAIL} settings.ACCOUNT_EMAIL_VERIFICATION = app_settings.EmailVerificationMethod.NONE user = user_factory(username="john", email="john@example.org", password="doe") c = Client() resp = c.post( reverse("account_signup"), { "username": "johndoe", "email": email_factory(email=user.email, mixed_case=True), "password1": "johndoe", "password2": "johndoe", }, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == settings.LOGIN_REDIRECT_URL assert EmailAddress.objects.filter(email="john@example.org").count() == 2 def test_prevent_enumeration_on(settings, user_factory, email_factory): settings.ACCOUNT_PREVENT_ENUMERATION = True settings.ACCOUNT_LOGIN_METHODS = {app_settings.LoginMethod.EMAIL} settings.ACCOUNT_EMAIL_VERIFICATION = app_settings.EmailVerificationMethod.NONE user = user_factory(username="john", email="john@example.org", password="doe") c = Client() resp = c.post( reverse("account_signup"), { "username": "johndoe", "email": email_factory(email=user.email, mixed_case=True), "password1": "johndoe", "password2": "johndoe", }, ) assert resp.status_code == HTTPStatus.OK assert resp.context["form"].errors == { "email": ["A user is already registered with this email address."] } @pytest.mark.django_db def test_get_initial_with_valid_email(): """Test that the email field is populated with a valid email.""" request = RequestFactory().get("/signup/?email=test@example.com") from allauth.account.views import signup SessionMiddleware(lambda request: None).process_request(request) request.user = AnonymousUser() with context.request_context(request): view = signup(request) assert view.context_data["view"].get_initial()["email"] == "test@example.com" def test_signup_user_model_no_email(settings, client, password_factory, db, mailoutbox): settings.ACCOUNT_USERNAME_REQUIRED = False settings.ACCOUNT_EMAIL_REQUIRED = True settings.ACCOUNT_EMAIL_VERIFICATION = app_settings.EmailVerificationMethod.MANDATORY settings.ACCOUNT_USER_MODEL_EMAIL_FIELD = None password = password_factory() email = "user@example.com" resp = client.post( reverse("account_signup"), { "email": email, "password1": password, "password2": password, }, ) assert resp.status_code == HTTPStatus.FOUND email = EmailAddress.objects.get(email=email) assert email.primary assert not email.verified assert len(mailoutbox) == 1 def test_email_lower_case(db, settings): settings.ACCOUNT_LOGIN_METHODS = {app_settings.LoginMethod.EMAIL} settings.ACCOUNT_EMAIL_VERIFICATION = app_settings.EmailVerificationMethod.NONE c = Client() resp = c.post( reverse("account_signup"), { "username": "johndoe", "email": "JoHn@DoE.oRg", "password1": "johndoe", "password2": "johndoe", }, ) assert resp.status_code == HTTPStatus.FOUND assert EmailAddress.objects.filter(email="john@doe.org").count() == 1 def test_does_not_create_user_when_honeypot_filled_out(client, db, settings): settings.ACCOUNT_SIGNUP_FORM_HONEYPOT_FIELD = "phone_number" resp = client.post( reverse("account_signup"), { "username": "johndoe", "email": "john@example.com", "password1": "Password1@", "password2": "Password1@", "phone_number": "5551231234", }, ) assert not get_user_model().objects.all().exists() assert resp.status_code == HTTPStatus.FOUND def test_create_user_when_honeypot_not_filled_out(client, db, settings): settings.ACCOUNT_SIGNUP_FORM_HONEYPOT_FIELD = "phone_number" resp = client.post( reverse("account_signup"), { "username": "johndoe", "email": "john@example.com", "password1": "Password1@", "password2": "Password1@", "phone_number": "", }, ) assert get_user_model().objects.filter(username="johndoe").count() == 1 assert resp.status_code == HTTPStatus.FOUND def test_signup_without_password( db, client, email_factory, settings_impacting_urls, ): with settings_impacting_urls( ACCOUNT_LOGIN_BY_CODE_ENABLED=True, ACCOUNT_EMAIL_VERIFICATION="mandatory", ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED=True, ACCOUNT_SIGNUP_FIELDS=["email*", "password1"], ): email = email_factory() resp = client.post( reverse("account_signup"), data={ "username": "wizard", "email": email, }, ) assert resp.status_code == HTTPStatus.FOUND user = get_user_model().objects.get(email=email) assert not user.check_password("") ================================================ FILE: tests/apps/account/test_utils.py ================================================ import uuid from unittest.mock import patch from django.contrib import messages from django.contrib.auth import get_user_model from django.contrib.messages.api import get_messages from django.contrib.messages.middleware import MessageMiddleware from django.contrib.sessions.middleware import SessionMiddleware from django.core import mail, validators from django.core.exceptions import ValidationError from django.template import Context, Template from django.test.client import RequestFactory from django.test.utils import override_settings from django.urls import reverse import pytest import allauth.app_settings from allauth.account.adapter import get_adapter from allauth.account.models import EmailAddress from allauth.account.utils import ( filter_users_by_username, url_str_to_user_pk, user_pk_to_url_str, user_username, ) from allauth.core import context from .test_models import UUIDUser test_username_validators = [ validators.RegexValidator(regex=r"^[a-c]+$", message="not abc") ] def test_url_str_to_pk_identifies_UUID_as_stringlike(db): with patch("allauth.account.utils.get_user_model") as mocked_gum: mocked_gum.return_value = UUIDUser user_id = uuid.uuid4().hex assert url_str_to_user_pk(user_id) == uuid.UUID(user_id) def test_pk_to_url_string_identifies_UUID_as_stringlike(): with patch("allauth.account.utils.get_user_model") as mocked_gum: mocked_gum.return_value = UUIDUser user = UUIDUser(is_active=True, email="john@example.com", username="john") assert user_pk_to_url_str(user) == user.pk.hex @override_settings(ACCOUNT_PRESERVE_USERNAME_CASING=False) def test_username_lower_cased(): user = get_user_model()() user_username(user, "CamelCase") assert user_username(user) == "camelcase" # TODO: Actually test something filter_users_by_username("CamelCase", "FooBar") @override_settings(ACCOUNT_PRESERVE_USERNAME_CASING=True) def test_username_case_preserved(): user = get_user_model()() user_username(user, "CamelCase") assert user_username(user) == "CamelCase" # TODO: Actually test something filter_users_by_username("camelcase", "foobar") def test_user_display(): user = get_user_model()(username="john
    doe") expected_name = "john<br/>doe" templates = [ "{% load account %}{% user_display user %}", "{% load account %}{% user_display user as x %}{{ x }}", ] for template in templates: t = Template(template) content = t.render(Context({"user": user})) assert content == expected_name def test_message_escaping(db): request = RequestFactory().get("/") SessionMiddleware(lambda request: None).process_request(request) MessageMiddleware(lambda request: None).process_request(request) user = get_user_model()() user_username(user, "'<8") context = {"user": user} get_adapter().add_message( request, messages.SUCCESS, "account/messages/logged_in.txt", context ) msgs = get_messages(request) actual_message = msgs._queued_messages[0].message assert user.username in actual_message, actual_message def test_email_escaping(db): site_name = "testserver" if allauth.app_settings.SITES_ENABLED: from django.contrib.sites.models import Site site = Site.objects.get_current() site.name = site_name = '' site.save() u = get_user_model().objects.create(username="test", email="user@example.com") request = RequestFactory().get("/") SessionMiddleware(lambda request: None).process_request(request) MessageMiddleware(lambda request: None).process_request(request) EmailAddress.objects.add_email(request, u, u.email, confirm=True) assert mail.outbox[0].subject[1:].startswith(site_name) @override_settings( ACCOUNT_USERNAME_VALIDATORS="tests.apps.account.test_utils.test_username_validators" ) def test_username_validator(db): get_adapter().clean_username("abc") with pytest.raises(ValidationError): get_adapter().clean_username("def") @override_settings(ALLOWED_HOSTS=["allowed_host", "testserver"]) def test_is_safe_url_no_wildcard(): with context.request_context(RequestFactory().get("/")): assert get_adapter().is_safe_url("http://allowed_host/") assert not get_adapter().is_safe_url("http://other_host/") def test_is_safe_url_subdomain(settings): settings.ALLOWED_HOSTS = [".example.com", "testserver"] with context.request_context(RequestFactory().get("/")): assert get_adapter().is_safe_url("http://bla.example.com") assert get_adapter().is_safe_url("http://example.com") assert not get_adapter().is_safe_url("http://not-example.com") @override_settings(ALLOWED_HOSTS=["*"]) def test_is_safe_url_wildcard(): with context.request_context(RequestFactory().get("/")): assert get_adapter().is_safe_url("http://foobar.com/") assert get_adapter().is_safe_url("http://other_host/") @override_settings(ALLOWED_HOSTS=["allowed_host", "testserver"]) def test_is_safe_url_relative_path(): with context.request_context(RequestFactory().get("/")): assert get_adapter().is_safe_url("/foo/bar") def test_redirect_noreversematch(auth_client): # We used to call `django.shortcuts.redirect()` as is, but that one throws a # `NoReverseMatch`, resulting in 500s. resp = auth_client.post(f"{reverse('account_logout')}?next=badurlname") assert resp["location"] == "/badurlname" ================================================ FILE: tests/apps/core/__init__.py ================================================ ================================================ FILE: tests/apps/core/internal/__init__.py ================================================ ================================================ FILE: tests/apps/core/internal/test_cryptokit.py ================================================ import pytest from allauth.core.internal.cryptokit import compare_user_code, generate_user_code @pytest.mark.parametrize( "actual,expected,result", [ ("xkcd", "XKCD", True), ("XKCD", "xkcd", True), ("XK-CD", "XKCD", True), ("XK CD", "XKCD", True), ("XK - CD", "XKCD", True), ("XKCD", "XK-CD", True), ("ABCD", "XKCD", False), ("", "XKCD", False), ("XKCD", "", False), ("", "", False), ], ) def test_compare_user_code(actual, expected, result): assert compare_user_code(actual=actual, expected=expected) is result @pytest.mark.parametrize( "length,result", [ (2, "X-X"), (3, "XX-X"), (6, "XXX-XXX"), (8, "XXXX-XXXX"), (9, "XXX-XXX-XXX"), (15, "XXXXX-XXXXX-XXXXX"), ], ) def test_generate_user_code_dashing(length, result): assert generate_user_code(length=length, allowed_chars="X", dashed=True) == result ================================================ FILE: tests/apps/core/internal/test_httpkit.py ================================================ import json from django.http import HttpRequest import pytest from allauth.core.internal import httpkit @pytest.mark.parametrize( "url,params,expected_url", [ ("/", {"foo": "bar", "v": 1}, "/?foo=bar&v=1"), ( "https://fqdn/?replace=this", {"replace": "that"}, "https://fqdn/?replace=that", ), ], ) def test_add_query_params(url, params, expected_url): assert httpkit.add_query_params(url, params) == expected_url @pytest.mark.parametrize( "url_template,kwargs,expected_url", [ ("/foo", {}, "http://testserver/foo"), ("/foo?key={key}", {"key": " "}, "http://testserver/foo?key=+"), ("/foo/{key}", {"key": " "}, "http://testserver/foo/%20"), ("https://abs.org/foo?key={key}", {"key": " "}, "https://abs.org/foo?key=+"), ], ) def test_render_url(url_template, kwargs, expected_url, rf): request = rf.get("/") assert httpkit.render_url(request, url_template, **kwargs) == expected_url def test_deserialize_request(rf): request = rf.get("/") assert not request.is_secure() serialized = httpkit.serialize_request(request) assert not httpkit.deserialize_request(serialized, HttpRequest()).is_secure() data = json.loads(serialized) data["scheme"] = "https" assert httpkit.deserialize_request(json.dumps(data), HttpRequest()).is_secure() ================================================ FILE: tests/apps/core/internal/test_modelkit.py ================================================ from datetime import date, datetime from django.contrib.postgres.fields import ArrayField from django.core.files.base import ContentFile from django.db import models from allauth.core.internal import modelkit def test_serializer(): class SomeValue: pass some_value = SomeValue() class SomeField(models.Field): def get_prep_value(self, value): return "somevalue" def from_db_value(self, value, expression, connection): return some_value class SomeModel(models.Model): dt = models.DateTimeField() t = models.TimeField() d = models.DateField() img1 = models.ImageField() img2 = models.ImageField() img3 = models.ImageField() something = SomeField() ips = ArrayField(models.GenericIPAddressField(), default=list, blank=True) class Meta: app_label = "dummy" def method(self): pass instance = SomeModel( dt=datetime.now(), d=date.today(), something=some_value, t=datetime.now().time(), ips=["1.1.1.1", "1.2.3.4"], ) instance.img1 = ContentFile(b"%PDF", name="foo.pdf") instance.img2 = ContentFile( b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x01\x00" b"\x00\x00\x007n\xf9$\x00\x00\x00\nIDATx\x9cc`\x00\x00\x00\x02\x00\x01H\xaf" b"\xa4q\x00\x00\x00\x00IEND\xaeB`\x82", name="foo.png", ) # make sure serializer doesn't fail if a method is attached to # the instance instance.method = method instance.nonfield = "hello" data = modelkit.serialize_instance(instance) instance2 = modelkit.deserialize_instance(SomeModel, data) assert getattr(instance, "method", None) == method assert getattr(instance2, "method", None) is None assert instance2.something == some_value assert instance2.img1.name == "foo.pdf" assert instance2.img2.name == "foo.png" assert instance2.img3.name == "" assert instance.nonfield == instance2.nonfield assert instance.d == instance2.d assert instance.dt.date() == instance2.dt.date() assert instance.ips == instance2.ips for t1, t2 in [ (instance.t, instance2.t), (instance.dt.time(), instance2.dt.time()), ]: assert t1.hour == t2.hour assert t1.minute == t2.minute assert t1.second == t2.second # AssertionError: datetime.time(10, 6, 28, 705776) # != datetime.time(10, 6, 28, 705000) assert int(t1.microsecond / 1000) == int(t2.microsecond / 1000) def test_serializer_binary_field(): class SomeBinaryModel(models.Model): bb = models.BinaryField() bb_empty = models.BinaryField() class Meta: app_label = "dummy" instance = SomeBinaryModel(bb=b"some binary data") serialized = modelkit.serialize_instance(instance) deserialized = modelkit.deserialize_instance(SomeBinaryModel, serialized) assert serialized["bb"] == "c29tZSBiaW5hcnkgZGF0YQ==" assert serialized["bb_empty"] == "" assert deserialized.bb == b"some binary data" assert deserialized.bb_empty == b"" ================================================ FILE: tests/apps/core/internal/test_ratelimit.py ================================================ import pytest from allauth.core.internal import ratelimit def test_rollback_consume(rf, enable_cache): def consume(): request = rf.post("/") config = {"foo": "2/m/ip"} return ratelimit.consume(request, config=config, action="foo") usage1 = consume() assert len(usage1.usage) > 0 usage2 = consume() assert len(usage2.usage) > 0 no_usage = consume() assert no_usage is None usage1.rollback() assert consume() assert not consume() @pytest.mark.parametrize( "rate,values", [ ("5/m", [(5, 60, "ip")]), ("5/m/user", [(5, 60, "user")]), ("2/3.5m/key", [(2, 210, "key")]), ("3/5m/user,20/0.5m/ip", [(3, 300, "user"), (20, 30, "ip")]), ("7/2h", [(7, 7200, "ip")]), ("7/0.25d", [(7, 21600, "ip")]), ], ) def test_parse(rate, values): rates = ratelimit.parse_rates(rate) assert len(rates) == len(values) for i, rate in enumerate(rates): assert rate.amount == values[i][0] assert rate.duration == values[i][1] assert rate.per == values[i][2] ================================================ FILE: tests/apps/headless/__init__.py ================================================ ================================================ FILE: tests/apps/headless/account/__init__.py ================================================ ================================================ FILE: tests/apps/headless/account/test_change_email.py ================================================ from http import HTTPStatus from django.contrib.auth import get_user_model from allauth.account.models import EmailAddress def test_list_email(auth_client, user, headless_reverse): resp = auth_client.get( headless_reverse("headless:account:manage_email"), ) assert len(resp.json()["data"]) == 1 def test_list_email_anon(db, client, headless_reverse): resp = client.get( headless_reverse("headless:account:manage_email"), ) assert resp.status_code == HTTPStatus.UNAUTHORIZED def test_remove_email(auth_client, user, email_factory, headless_reverse): addr = EmailAddress.objects.create(email=email_factory(), user=user) assert EmailAddress.objects.filter(user=user).count() == 2 resp = auth_client.delete( headless_reverse("headless:account:manage_email"), data={"email": addr.email}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert len(resp.json()["data"]) == 1 assert not EmailAddress.objects.filter(pk=addr.pk).exists() def test_add_email(auth_client, user, email_factory, headless_reverse): new_email = email_factory() resp = auth_client.post( headless_reverse("headless:account:manage_email"), data={"email": new_email}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert len(resp.json()["data"]) == 2 assert EmailAddress.objects.filter(email=new_email, verified=False).exists() def test_change_primary(auth_client, user, email_factory, headless_reverse): addr = EmailAddress.objects.create( email=email_factory(), user=user, verified=True, primary=False ) resp = auth_client.patch( headless_reverse("headless:account:manage_email"), data={"email": addr.email, "primary": True}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert len(resp.json()["data"]) == 2 assert EmailAddress.objects.filter(pk=addr.pk, primary=True).exists() def test_resend_verification( auth_client, user, email_factory, headless_reverse, mailoutbox ): addr = EmailAddress.objects.create(email=email_factory(), user=user, verified=False) resp = auth_client.put( headless_reverse("headless:account:manage_email"), data={"email": addr.email}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert len(mailoutbox) == 1 def test_email_rate_limit( auth_client, user, email_factory, headless_reverse, settings, enable_cache ): settings.ACCOUNT_RATE_LIMITS = {"manage_email": "1/m/ip"} for attempt in range(2): new_email = email_factory() resp = auth_client.post( headless_reverse("headless:account:manage_email"), data={"email": new_email}, content_type="application/json", ) expected_status = ( HTTPStatus.OK if attempt == 0 else HTTPStatus.TOO_MANY_REQUESTS ) assert resp.status_code == expected_status assert resp.json()["status"] == expected_status def test_resend_verification_rate_limit( auth_client, user, email_factory, headless_reverse, settings, enable_cache, mailoutbox, ): settings.ACCOUNT_RATE_LIMITS = {"confirm_email": "1/m/ip"} for attempt in range(2): addr = EmailAddress.objects.create( email=email_factory(), user=user, verified=False ) resp = auth_client.put( headless_reverse("headless:account:manage_email"), data={"email": addr.email}, content_type="application/json", ) assert resp.status_code == HTTPStatus.FORBIDDEN if attempt else HTTPStatus.OK assert len(mailoutbox) == 1 def test_change_email_to_conflicting( settings, auth_client, user, email_factory, headless_reverse, user_factory, get_last_email_verification_code, mailoutbox, ): other_user = user_factory(email="taken@conflict.org") settings.ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True settings.ACCOUNT_CHANGE_EMAIL = True resp = auth_client.post( headless_reverse("headless:account:manage_email"), data={"email": other_user.email}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert len(resp.json()["data"]) == 2 assert EmailAddress.objects.filter(user=user).count() == 1 code = get_last_email_verification_code(auth_client, mailoutbox) resp = auth_client.post( headless_reverse("headless:account:verify_email"), data={"key": code}, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "status": HTTPStatus.BAD_REQUEST, "errors": [ { "message": "A user is already registered with this email address.", "code": "email_taken", "param": "key", } ], } def test_change_email_by_code( settings, auth_client, user, email_factory, headless_reverse, user_factory, get_last_email_verification_code, mailoutbox, ): settings.ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True settings.ACCOUNT_CHANGE_EMAIL = True new_email = email_factory() resp = auth_client.post( headless_reverse("headless:account:manage_email"), data={"email": new_email}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert len(resp.json()["data"]) == 2 assert EmailAddress.objects.filter(user=user).count() == 1 code = get_last_email_verification_code(auth_client, mailoutbox) resp = auth_client.post( headless_reverse("headless:account:verify_email"), data={"key": code}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert EmailAddress.objects.filter(user=user).count() == 1 assert EmailAddress.objects.filter(user=user, email=new_email).exists() def test_change_email_at_signup( settings, client, user, email_factory, headless_reverse, user_factory, get_last_email_verification_code, mailoutbox, password_factory, ): settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True settings.ACCOUNT_CHANGE_EMAIL = True settings.ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_CHANGE = True email = email_factory() resp = client.post( headless_reverse("headless:account:signup"), data={ "username": "wizard", "email": email, "password": password_factory(), }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED user = get_user_model().objects.last() assert EmailAddress.objects.filter(user=user).count() == 1 assert EmailAddress.objects.filter(user=user, email=email, verified=False).exists() new_email = email_factory() resp = client.post( headless_reverse("headless:account:manage_email"), data={"email": new_email}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert len(resp.json()["data"]) == 1 assert EmailAddress.objects.filter(user=user).count() == 1 assert EmailAddress.objects.filter( user=user, email=new_email, verified=False ).exists() code = get_last_email_verification_code(client, mailoutbox) resp = client.post( headless_reverse("headless:account:verify_email"), data={"key": code}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert EmailAddress.objects.filter(user=user).count() == 1 assert EmailAddress.objects.filter( user=user, email=new_email, verified=True ).exists() ================================================ FILE: tests/apps/headless/account/test_change_password.py ================================================ import copy from http import HTTPStatus from unittest.mock import ANY import pytest @pytest.mark.parametrize( "has_password,request_data,response_data,status_code", [ # Wrong current password ( True, {"current_password": "wrong", "new_password": "{password_factory}"}, { "status": HTTPStatus.BAD_REQUEST, "errors": [ { "param": "current_password", "message": "Please type your current password.", "code": "enter_current_password", } ], }, HTTPStatus.BAD_REQUEST, ), # Happy flow, regular password change ( True, { "current_password": "{user_password}", "new_password": "{password_factory}", }, { "status": HTTPStatus.OK, "meta": {"is_authenticated": True}, "data": { "user": ANY, "methods": [], }, }, HTTPStatus.OK, ), # New password does not match constraints ( True, { "current_password": "{user_password}", "new_password": "a", }, { "status": HTTPStatus.BAD_REQUEST, "errors": [ { "param": "new_password", "code": "password_too_short", "message": "This password is too short. It must contain at least 6 characters.", } ], }, HTTPStatus.BAD_REQUEST, ), # New password not empty ( True, { "current_password": "{user_password}", "new_password": "", }, { "status": HTTPStatus.BAD_REQUEST, "errors": [ { "param": "new_password", "code": "required", "message": "This field is required.", } ], }, HTTPStatus.BAD_REQUEST, ), # Current password not blank ( True, { "current_password": "", "new_password": "{password_factory}", }, { "status": HTTPStatus.BAD_REQUEST, "errors": [ { "param": "current_password", "message": "This field is required.", "code": "required", } ], }, HTTPStatus.BAD_REQUEST, ), # Current password missing ( True, { "new_password": "{password_factory}", }, { "status": HTTPStatus.BAD_REQUEST, "errors": [ { "param": "current_password", "message": "This field is required.", "code": "required", } ], }, HTTPStatus.BAD_REQUEST, ), # Current password not set, happy flow ( False, { "current_password": "", "new_password": "{password_factory}", }, { "status": HTTPStatus.OK, "meta": {"is_authenticated": True}, "data": { "user": ANY, "methods": [], }, }, HTTPStatus.OK, ), # Current password not set, current_password absent ( False, { "new_password": "{password_factory}", }, { "status": HTTPStatus.OK, "meta": {"is_authenticated": True}, "data": { "user": ANY, "methods": [], }, }, HTTPStatus.OK, ), ], ) def test_change_password( auth_client, user, request_data, response_data, status_code, has_password, user_password, password_factory, settings, mailoutbox, headless_reverse, headless_client, ): request_data = copy.deepcopy(request_data) response_data = copy.deepcopy(response_data) settings.ACCOUNT_EMAIL_NOTIFICATIONS = True if not has_password: user.set_unusable_password() user.save(update_fields=["password"]) auth_client.force_login(user) if request_data.get("current_password") == "{user_password}": request_data["current_password"] = user_password if request_data.get("new_password") == "{password_factory}": request_data["new_password"] = password_factory() resp = auth_client.post( headless_reverse("headless:account:change_password"), data=request_data, content_type="application/json", ) assert resp.status_code == status_code resp_json = resp.json() if headless_client == "app" and resp.status_code == HTTPStatus.OK: response_data["meta"]["session_token"] = ANY assert resp_json == response_data user.refresh_from_db() if resp.status_code == HTTPStatus.OK: assert user.check_password(request_data["new_password"]) assert len(mailoutbox) == 1 else: assert user.check_password(user_password) assert len(mailoutbox) == 0 def test_change_password_rate_limit( enable_cache, auth_client, user, user_password, password_factory, settings, headless_reverse, ): settings.ACCOUNT_RATE_LIMITS = {"change_password": "1/m/ip"} for attempt in range(2): new_password = password_factory() resp = auth_client.post( headless_reverse("headless:account:change_password"), data={ "current_password": user_password, "new_password": new_password, }, content_type="application/json", ) user_password = new_password expected_status = ( HTTPStatus.OK if attempt == 0 else HTTPStatus.TOO_MANY_REQUESTS ) assert resp.status_code == expected_status assert resp.json()["status"] == expected_status ================================================ FILE: tests/apps/headless/account/test_email_verification.py ================================================ from http import HTTPStatus import pytest from allauth.account.models import ( EmailAddress, EmailConfirmationHMAC, get_emailconfirmation_model, ) from allauth.headless.constants import Flow def test_verify_email_other_user(auth_client, user, user_factory, headless_reverse): other_user = user_factory(email_verified=False) email_address = EmailAddress.objects.get(user=other_user, verified=False) assert not email_address.verified key = EmailConfirmationHMAC(email_address).key resp = auth_client.post( headless_reverse("headless:account:verify_email"), data={"key": key}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK data = resp.json() # We're still authenticated as the user originally logged in, not the # other_user. assert data["data"]["user"]["id"] == user.pk @pytest.mark.parametrize( "login_on_email_verification,status_code", [(False, HTTPStatus.UNAUTHORIZED), (True, HTTPStatus.OK)], ) def test_auth_unverified_email( client, user_factory, password_factory, settings, headless_reverse, login_on_email_verification, status_code, ): settings.ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = login_on_email_verification settings.ACCOUNT_LOGIN_METHODS = {"email"} settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" password = password_factory() user = user_factory(email_verified=False, password=password) resp = client.post( headless_reverse("headless:account:login"), data={ "email": user.email, "password": password, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED data = resp.json() flows = data["data"]["flows"] assert [f for f in flows if f["id"] == Flow.VERIFY_EMAIL][0]["is_pending"] emailaddress = EmailAddress.objects.filter(user=user, verified=False).get() key = get_emailconfirmation_model().create(emailaddress).key resp = client.post( headless_reverse("headless:account:verify_email"), data={"key": key}, content_type="application/json", ) assert resp.status_code == status_code def test_verify_email_bad_key( client, settings, password_factory, user_factory, headless_reverse ): settings.ACCOUNT_LOGIN_METHODS = {"email"} settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" password = password_factory() user = user_factory(email_verified=False, password=password) resp = client.post( headless_reverse("headless:account:login"), data={ "email": user.email, "password": password, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED resp = client.get( headless_reverse("headless:account:verify_email"), data={"key": "bad"}, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST resp = client.post( headless_reverse("headless:account:verify_email"), data={"key": "bad"}, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "status": HTTPStatus.BAD_REQUEST, "errors": [ { "code": "invalid_or_expired_key", "param": "key", "message": "Invalid or expired key.", } ], } ================================================ FILE: tests/apps/headless/account/test_email_verification_by_code.py ================================================ from http import HTTPStatus from unittest.mock import ANY from django.contrib.auth.models import User import pytest from pytest_django.asserts import assertTemplateNotUsed, assertTemplateUsed from allauth.account import app_settings from allauth.account.models import EmailAddress from allauth.headless.constants import Flow def test_email_verification_rate_limits_login( client, db, user_password, settings, user_factory, password_factory, enable_cache, headless_reverse, ): settings.ACCOUNT_AUTHENTICATION_METHOD = "email" settings.ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.ACCOUNT_RATE_LIMITS = {"confirm_email": "1/m/key"} email = "user@email.org" user_factory(email=email, email_verified=False, password=user_password) for attempt in range(2): resp = client.post( headless_reverse("headless:account:login"), data={ "email": email, "password": user_password, }, content_type="application/json", ) if attempt == 0: assert resp.status_code == HTTPStatus.UNAUTHORIZED flow = [ flow for flow in resp.json()["data"]["flows"] if flow.get("is_pending") ][0] assert flow["id"] == Flow.VERIFY_EMAIL else: assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "status": HTTPStatus.BAD_REQUEST, "errors": [ { "message": "Too many failed login attempts. Try again later.", "code": "too_many_login_attempts", } ], } @pytest.mark.parametrize("method", ["GET", "POST"]) def test_email_verification_rate_limits_submitting_codes( client, db, user_password, settings, user_factory, password_factory, enable_cache, headless_reverse, method, ): settings.ACCOUNT_AUTHENTICATION_METHOD = "email" settings.ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.ACCOUNT_RATE_LIMITS = {"confirm_email": "1/m/key"} email = "user@email.org" user_factory(email=email, email_verified=False, password=user_password) resp = client.post( headless_reverse("headless:account:login"), data={ "email": email, "password": user_password, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED flow = [flow for flow in resp.json()["data"]["flows"] if flow.get("is_pending")][0] assert flow["id"] == Flow.VERIFY_EMAIL for i in range(app_settings.EMAIL_VERIFICATION_BY_CODE_MAX_ATTEMPTS): if method == "GET": resp = client.get( headless_reverse("headless:account:verify_email"), HTTP_X_EMAIL_VERIFICATION_KEY="123", ) else: resp = client.post( headless_reverse("headless:account:verify_email"), data={ "key": "123", }, content_type="application/json", ) if i < app_settings.EMAIL_VERIFICATION_BY_CODE_MAX_ATTEMPTS: assert resp.json() == { "status": HTTPStatus.BAD_REQUEST, "errors": [ { "message": "Incorrect code.", "code": "incorrect_code", "param": "key", } ], } assert resp.status_code == HTTPStatus.BAD_REQUEST else: assert resp.status_code == HTTPStatus.CONFLICT def test_add_email( auth_client, user, email_factory, headless_reverse, settings, get_last_email_verification_code, mailoutbox, ): settings.ACCOUNT_AUTHENTICATION_METHOD = "email" settings.ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True settings.ACCOUNT_CHANGE_EMAIL = True new_email = email_factory() # Let's add an email... resp = auth_client.post( headless_reverse("headless:account:manage_email"), data={"email": new_email}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assertTemplateNotUsed(resp, "account/email/email_confirmation_signup_message.txt") assertTemplateUsed(resp, "account/email/email_confirmation_message.txt") # It's in the response, albeit unverified. assert len(resp.json()["data"]) == 2 email_map = {addr["email"]: addr for addr in resp.json()["data"]} assert not email_map[new_email]["verified"] # Verify the email with an invalid code. resp = auth_client.post( headless_reverse("headless:account:verify_email"), data={"key": "key"}, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "status": HTTPStatus.BAD_REQUEST, "errors": [ {"message": "Incorrect code.", "code": "incorrect_code", "param": "key"} ], } # And with the valid code... code = get_last_email_verification_code(auth_client, mailoutbox) resp = auth_client.post( headless_reverse("headless:account:verify_email"), data={"key": code}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert resp.json()["data"]["user"]["email"] == new_email # ACCOUNT_CHANGE_EMAIL = True, so the other one is gone. assert EmailAddress.objects.filter(user=user).count() == 1 # Re-verification won't work... resp = auth_client.post( headless_reverse("headless:account:verify_email"), data={"key": code}, content_type="application/json", ) assert resp.status_code == HTTPStatus.CONFLICT @pytest.mark.parametrize("login_on_email_verification", [False, True]) def test_signup_with_email_verification( db, client, email_factory, password_factory, settings, headless_reverse, headless_client, get_last_email_verification_code, login_on_email_verification, mailoutbox, ): settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.ACCOUNT_USERNAME_REQUIRED = False # This setting should have no affect: settings.ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = login_on_email_verification settings.ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True email = email_factory() resp = client.post( headless_reverse("headless:account:signup"), data={ "email": email, "password": password_factory(), }, content_type="application/json", ) assertTemplateUsed(resp, "account/email/email_confirmation_signup_message.txt") assert resp.status_code == HTTPStatus.UNAUTHORIZED assert User.objects.filter(email=email).exists() data = resp.json() flow = next(f for f in data["data"]["flows"] if f.get("is_pending")) assert flow["id"] == "verify_email" code = get_last_email_verification_code(client, mailoutbox) resp = client.get( headless_reverse("headless:account:verify_email"), HTTP_X_EMAIL_VERIFICATION_KEY=code, ) assert resp.status_code == HTTPStatus.OK assert resp.json() == { "data": { "email": email, "user": ANY, }, "meta": {"is_authenticating": True}, "status": HTTPStatus.OK, } resp = client.post( headless_reverse("headless:account:verify_email"), data={"key": code}, content_type="application/json", ) addr = EmailAddress.objects.get(email=email) assert addr.verified assert resp.status_code == HTTPStatus.OK data = resp.json() assert data["meta"]["is_authenticated"] @pytest.mark.parametrize("rate_limit_enabled", [(False,), (True,)]) def test_resend_at_signup( db, client, email_factory, password_factory, settings, headless_reverse, headless_client, get_last_email_verification_code, mailoutbox, rate_limit_enabled, request, ): if rate_limit_enabled: request.getfixturevalue("enable_cache") settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.ACCOUNT_USERNAME_REQUIRED = False # This setting should have no affect: settings.ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_RESEND = True settings.ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True email = email_factory() resp = client.post( headless_reverse("headless:account:signup"), data={ "email": email, "password": password_factory(), }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED assert User.objects.filter(email=email).exists() data = resp.json() flow = next(f for f in data["data"]["flows"] if f.get("is_pending")) assert flow["id"] == "verify_email" code = get_last_email_verification_code(client, mailoutbox) resp = client.get( headless_reverse("headless:account:verify_email"), HTTP_X_EMAIL_VERIFICATION_KEY=code, ) assert resp.status_code == HTTPStatus.OK assert resp.json() == { "data": { "email": email, "user": ANY, }, "meta": {"is_authenticating": True}, "status": HTTPStatus.OK, } resp = client.post( headless_reverse("headless:account:resend_email_verification_code"), ) if rate_limit_enabled: assert resp.status_code == HTTPStatus.TOO_MANY_REQUESTS else: assert resp.status_code == HTTPStatus.OK new_code = get_last_email_verification_code(client, mailoutbox) assert code != new_code def test_add_resend_verify_email( auth_client, user, email_factory, headless_reverse, settings_impacting_urls, get_last_email_verification_code, mailoutbox, ): with settings_impacting_urls( ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED=True, ACCOUNT_EMAIL_VERIFICATION_SUPPORTS_RESEND=True, ): new_email = email_factory() resp = auth_client.post( headless_reverse("headless:account:manage_email"), data={"email": new_email}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert len(resp.json()["data"]) == 2 assert len(mailoutbox) == 1 assert not EmailAddress.objects.filter(email=new_email, verified=False).exists() code = get_last_email_verification_code(auth_client, mailoutbox) resp = auth_client.put( headless_reverse("headless:account:manage_email"), data={"email": new_email}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert len(mailoutbox) == 2 code2 = get_last_email_verification_code(auth_client, mailoutbox) assert code != code2 resp = auth_client.post( headless_reverse("headless:account:verify_email"), data={"key": code2}, content_type="application/json", ) assert EmailAddress.objects.filter(email=new_email, verified=True).exists() def test_remove_unverified_email( auth_client, user, email_factory, headless_reverse, settings, get_last_email_verification_code, mailoutbox, ): settings.ACCOUNT_AUTHENTICATION_METHOD = "email" settings.ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True settings.ACCOUNT_CHANGE_EMAIL = True new_email = email_factory() # Let's add an email... resp = auth_client.post( headless_reverse("headless:account:manage_email"), data={"email": new_email}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK # It's in the response, albeit unverified. assert len(resp.json()["data"]) == 2 email_map = {addr["email"]: addr for addr in resp.json()["data"]} assert not email_map[new_email]["verified"] # Delete the pending email. resp = auth_client.delete( headless_reverse("headless:account:manage_email"), data={"email": new_email}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert len(resp.json()["data"]) == 1 assert new_email not in {addr["email"] for addr in resp.json()["data"]} ================================================ FILE: tests/apps/headless/account/test_login.py ================================================ from http import HTTPStatus from unittest.mock import ANY import pytest from allauth.account.signals import user_logged_in from allauth.headless.base.response import AuthenticationResponse def test_auth_password_input_error(headless_reverse, client): resp = client.post( headless_reverse("headless:account:login"), data={}, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "status": HTTPStatus.BAD_REQUEST, "errors": [ { "message": "This field is required.", "code": "required", "param": "username", }, { "message": "This field is required.", "code": "required", "param": "password", }, ], } def test_auth_password_bad_password(headless_reverse, client, user, settings): settings.ACCOUNT_LOGIN_METHODS = {"email"} resp = client.post( headless_reverse("headless:account:login"), data={ "email": user.email, "password": "wrong", }, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "status": HTTPStatus.BAD_REQUEST, "errors": [ { "param": "password", "message": "The email address and/or password you specified are not correct.", "code": "email_password_mismatch", } ], } def test_auth_password_success( client, user, user_password, settings, headless_reverse, headless_client ): settings.ACCOUNT_LOGIN_METHODS = {"email", "username"} login_resp = client.post( headless_reverse("headless:account:login"), data={ "email": user.email, "password": user_password, }, content_type="application/json", ) assert login_resp.status_code == HTTPStatus.OK session_resp = client.get( headless_reverse("headless:account:current_session"), content_type="application/json", ) assert session_resp.status_code == HTTPStatus.OK for resp in [login_resp, session_resp]: extra_meta = {} if headless_client == "app" and resp == login_resp: # The session is created on first login, and hence the token is # exposed only at that moment. extra_meta["session_token"] = ANY assert resp.json() == { "status": HTTPStatus.OK, "data": { "user": { "id": user.pk, "display": str(user), "email": user.email, "username": user.username, "has_usable_password": True, }, "methods": [ { "at": ANY, "email": user.email, "method": "password", } ], }, "meta": {"is_authenticated": True, **extra_meta}, } @pytest.mark.parametrize( "is_active,status_code", [(False, HTTPStatus.UNAUTHORIZED), (True, HTTPStatus.OK)] ) def test_auth_password_user_inactive( client, user, user_password, settings, status_code, is_active, headless_reverse ): user.is_active = is_active user.save(update_fields=["is_active"]) resp = client.post( headless_reverse("headless:account:login"), data={ "username": user.username, "password": user_password, }, content_type="application/json", ) assert resp.status_code == status_code def test_login_failed_rate_limit( client, user, settings, headless_reverse, headless_client, enable_cache, ): settings.ACCOUNT_RATE_LIMITS = {"login_failed": "1/m/ip"} settings.ACCOUNT_LOGIN_METHODS = {"email"} for attempt in range(2): resp = client.post( headless_reverse("headless:account:login"), data={ "email": user.email, "password": "wrong", }, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json()["errors"] == [ ( { "code": "email_password_mismatch", "message": "The email address and/or password you specified are not correct.", "param": "password", } if attempt == 0 else { "message": "Too many failed login attempts. Try again later.", "code": "too_many_login_attempts", } ) ] def test_login_rate_limit( client, user, user_password, settings, headless_reverse, headless_client, enable_cache, ): settings.ACCOUNT_RATE_LIMITS = {"login": "1/m/ip"} settings.ACCOUNT_LOGIN_METHODS = {"email"} for attempt in range(2): resp = client.post( headless_reverse("headless:account:login"), data={ "email": user.email, "password": user_password, }, content_type="application/json", ) expected_status = HTTPStatus.TOO_MANY_REQUESTS if attempt else HTTPStatus.OK assert resp.status_code == expected_status def test_login_already_logged_in( auth_client, user, user_password, settings, headless_reverse ): settings.ACCOUNT_LOGIN_METHODS = {"email"} resp = auth_client.post( headless_reverse("headless:account:login"), data={ "email": user.email, "password": user_password, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.CONFLICT def test_custom_post_login_response( settings, client, headless_reverse, user, user_password ): settings.ACCOUNT_LOGIN_METHODS = {"email"} def on_user_logged_in(**kwargs): response = kwargs["response"] assert isinstance(response, AuthenticationResponse) user_logged_in.connect(on_user_logged_in) try: login_resp = client.post( headless_reverse("headless:account:login"), data={ "email": user.email, "password": user_password, }, content_type="application/json", ) assert login_resp.status_code == HTTPStatus.OK finally: user_logged_in.disconnect(on_user_logged_in) ================================================ FILE: tests/apps/headless/account/test_login_by_code.py ================================================ import time from http import HTTPStatus import pytest from allauth.account.internal.stagekit import LOGIN_SESSION_KEY from allauth.account.models import EmailAddress from allauth.account.stages import LoginByCodeStage from allauth.headless.constants import Flow @pytest.fixture def get_last_login_code(mailoutbox): def f(): return [line for line in mailoutbox[-1].body.splitlines() if len(line) == 9][0] return f def test_login_by_code(headless_reverse, user, client, mailoutbox, get_last_login_code): resp = client.post( headless_reverse("headless:account:request_login_code"), data={"email": user.email}, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED data = resp.json() assert [f for f in data["data"]["flows"] if f["id"] == Flow.LOGIN_BY_CODE][0][ "is_pending" ] assert len(mailoutbox) == 1 code = get_last_login_code() resp = client.post( headless_reverse("headless:account:confirm_login_code"), data={"code": code}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK data = resp.json() assert data["meta"]["is_authenticated"] def test_login_by_code_rate_limit( headless_reverse, user, client, mailoutbox, settings, enable_cache ): settings.ACCOUNT_RATE_LIMITS = {"request_login_code": "1/m/ip"} for attempt in range(2): resp = client.post( headless_reverse("headless:account:request_login_code"), data={"email": user.email}, content_type="application/json", ) expected_code = HTTPStatus.BAD_REQUEST if attempt else HTTPStatus.UNAUTHORIZED assert resp.status_code == expected_code data = resp.json() assert data["status"] == expected_code if expected_code == HTTPStatus.BAD_REQUEST: assert data["errors"] == [ { "code": "too_many_login_attempts", "message": "Too many failed login attempts. Try again later.", "param": "email", }, ] def test_login_by_code_max_attemps(headless_reverse, user, client, settings): settings.ACCOUNT_LOGIN_BY_CODE_MAX_ATTEMPTS = 2 resp = client.post( headless_reverse("headless:account:request_login_code"), data={"email": user.email}, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED for i in range(3): resp = client.post( headless_reverse("headless:account:confirm_login_code"), data={"code": "wrong"}, content_type="application/json", ) session_resp = client.get( headless_reverse("headless:account:current_session"), data={"code": "wrong"}, content_type="application/json", ) assert session_resp.status_code == HTTPStatus.UNAUTHORIZED pending_flows = [ f for f in session_resp.json()["data"]["flows"] if f.get("is_pending") ] if i >= 1: assert ( resp.status_code == HTTPStatus.CONFLICT if i >= 2 else HTTPStatus.BAD_REQUEST ) assert len(pending_flows) == 0 else: assert resp.status_code == HTTPStatus.BAD_REQUEST assert len(pending_flows) == 1 def test_login_by_code_required( client, settings, user_factory, password_factory, headless_reverse, mailoutbox ): settings.ACCOUNT_LOGIN_BY_CODE_REQUIRED = True password = password_factory() user = user_factory(password=password, email_verified=False) email_address = EmailAddress.objects.get(email=user.email) assert not email_address.verified resp = client.post( headless_reverse("headless:account:login"), data={ "username": user.username, "password": password, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED pending_flow = [f for f in resp.json()["data"]["flows"] if f.get("is_pending")][0][ "id" ] assert pending_flow == Flow.LOGIN_BY_CODE code = [line for line in mailoutbox[0].body.splitlines() if len(line) == 9][0] resp = client.post( headless_reverse("headless:account:confirm_login_code"), data={"code": code}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK data = resp.json() assert data["meta"]["is_authenticated"] email_address.refresh_from_db() assert email_address.verified def test_login_by_code_expired(headless_reverse, user, client, mailoutbox): resp = client.post( headless_reverse("headless:account:request_login_code"), data={"email": user.email}, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED data = resp.json() assert [f for f in data["data"]["flows"] if f["id"] == Flow.LOGIN_BY_CODE][0][ "is_pending" ] assert len(mailoutbox) == 1 code = [line for line in mailoutbox[0].body.splitlines() if len(line) == 9][0] # Expire code session = client.headless_session() login = session[LOGIN_SESSION_KEY] login["state"]["stages"][LoginByCodeStage.key]["data"]["at"] = ( time.time() - 24 * 60 * 60 ) session["account_login"] = login session.save() # Post valid code resp = client.post( headless_reverse("headless:account:confirm_login_code"), data={"code": code}, content_type="application/json", ) assert resp.status_code == HTTPStatus.CONFLICT def test_post_login_code_when_flow_not_pending( db, client, settings_impacting_urls, headless_reverse, mailoutbox, get_last_email_verification_code, ): with settings_impacting_urls( ACCOUNT_LOGIN_METHODS={"email"}, ACCOUNT_SIGNUP_FIELDS=["email*"], ACCOUNT_EMAIL_VERIFICATION="mandatory", ACCOUNT_LOGIN_BY_CODE_ENABLED=True, ACCOUNT_LOGIN_BY_CODE_REQUIRED=False, ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED=True, ): resp = client.post( headless_reverse("headless:account:signup"), data={"email": "user@email.org"}, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED resp = client.post( headless_reverse("headless:account:confirm_login_code"), data={"code": "123"}, content_type="application/json", ) assert resp.status_code == HTTPStatus.CONFLICT def test_login_by_code_resend_limited( settings, headless_reverse, user, client, mailoutbox, get_last_login_code ): settings.ACCOUNT_LOGIN_BY_CODE_SUPPORTS_RESEND = 2 resp = client.post( headless_reverse("headless:account:request_login_code"), data={"email": user.email}, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED code = get_last_login_code() for i in range(3): resp = client.post( headless_reverse("headless:account:resend_login_code"), ) assert resp.status_code == (HTTPStatus.CONFLICT if i == 2 else HTTPStatus.OK) new_code = get_last_login_code() if i == 2: assert new_code == code else: assert new_code != code code = new_code ================================================ FILE: tests/apps/headless/account/test_phone.py ================================================ from http import HTTPStatus from django.contrib.auth import get_user_model import pytest from allauth.account.adapter import get_adapter as get_account_adapter def test_change_phone_to_same( auth_client, user_with_phone, phone, settings_impacting_urls, headless_reverse ): with settings_impacting_urls( ACCOUNT_SIGNUP_FIELDS=["phone*"], ACCOUNT_LOGIN_METHODS=("phone",), ): resp = auth_client.post( headless_reverse("headless:account:manage_phone"), data={ "phone": phone, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "errors": [ { "code": "same_as_current", "message": "The new value must be different from the current one.", "param": "phone", } ], "status": HTTPStatus.BAD_REQUEST, } def test_change_phone( auth_client, user_with_phone, phone, settings_impacting_urls, headless_reverse, phone_factory, sms_outbox, ): with settings_impacting_urls( ACCOUNT_SIGNUP_FIELDS=["phone*"], ACCOUNT_LOGIN_METHODS=("phone",), ): new_phone = phone_factory() resp = auth_client.post( headless_reverse("headless:account:manage_phone"), data={ "phone": new_phone, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.ACCEPTED assert resp.json() == { "data": [ { "phone": new_phone, "verified": False, }, ], "status": HTTPStatus.ACCEPTED, } resp = auth_client.post( headless_reverse("headless:account:verify_phone"), data={ "code": sms_outbox[-1]["code"], }, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK resp = auth_client.post( headless_reverse("headless:account:verify_phone"), data={ "code": sms_outbox[-1]["code"], }, content_type="application/json", ) assert resp.status_code == HTTPStatus.CONFLICT resp = auth_client.get( headless_reverse("headless:account:manage_phone"), content_type="application/json", ) assert resp.json() == { "data": [ { "phone": new_phone, "verified": True, }, ], "status": HTTPStatus.OK, } def test_login( client, user_with_phone, phone, user_password, headless_reverse, settings_impacting_urls, ): with settings_impacting_urls( ACCOUNT_SIGNUP_FIELDS=["phone*", "password1*"], ACCOUNT_LOGIN_METHODS=("phone",), ): resp = client.post( headless_reverse("headless:account:login"), data={"phone": phone, "password": user_password}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK def test_login_by_code( client, user_with_phone, phone, headless_reverse, settings_impacting_urls, sms_outbox, ): with settings_impacting_urls( ACCOUNT_SIGNUP_FIELDS=["phone*"], ACCOUNT_LOGIN_METHODS=("phone",), ): resp = client.post( headless_reverse("headless:account:request_login_code"), data={ "phone": phone, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED code = sms_outbox[-1]["code"] resp = client.post( headless_reverse("headless:account:confirm_login_code"), data={"code": code}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK data = resp.json() assert data["meta"]["is_authenticated"] def test_signup( db, client, settings_impacting_urls, phone, headless_reverse, headless_client, password_factory, sms_outbox, ): with settings_impacting_urls( ACCOUNT_SIGNUP_FIELDS=["phone*", "password1*"], ACCOUNT_LOGIN_METHODS=("phone",), ): resp = client.post( headless_reverse("headless:account:signup"), data={ "phone": phone, "password": password_factory(), }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED pending_flow = [ flow for flow in resp.json()["data"]["flows"] if flow.get("is_pending") ][0] assert pending_flow["id"] == "verify_phone" assert len(sms_outbox) == 1 code = sms_outbox[-1]["code"] resp = client.post( headless_reverse("headless:account:verify_phone"), data={ "code": code, }, content_type="application/json", ) assert resp.json()["status"] == HTTPStatus.OK def test_reauthentication( auth_client, user_with_phone, phone, settings_impacting_urls, headless_reverse, phone_factory, sms_outbox, ): with settings_impacting_urls( ACCOUNT_REAUTHENTICATION_REQUIRED=True, ACCOUNT_SIGNUP_FIELDS=["phone*"], ACCOUNT_LOGIN_METHODS=("phone",), ): new_phone = phone_factory() resp = auth_client.post( headless_reverse("headless:account:manage_phone"), data={ "phone": new_phone, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED assert resp.json()["data"]["flows"] == [{"id": "reauthenticate"}] def test_change_phone_to_conflicting( auth_client, user_with_phone, phone, settings_impacting_urls, headless_reverse, user_factory, phone_factory, sms_outbox, ): other_phone = phone_factory() user_factory(phone=other_phone) with settings_impacting_urls( ACCOUNT_SIGNUP_FIELDS=["phone*"], ACCOUNT_LOGIN_METHODS=("phone",), ): resp = auth_client.post( headless_reverse("headless:account:manage_phone"), data={ "phone": other_phone, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.ACCEPTED assert resp.json() == { "data": [{"phone": other_phone, "verified": False}], "status": HTTPStatus.ACCEPTED, } resp = auth_client.post( headless_reverse("headless:account:verify_phone"), data={ "code": sms_outbox[-1]["code"], }, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "errors": [ { "code": "phone_taken", "message": "A user is already registered with this phone number.", "param": "code", } ], "status": HTTPStatus.BAD_REQUEST, } def test_change_at_signup( db, client, settings_impacting_urls, phone, headless_reverse, headless_client, password_factory, sms_outbox, phone_factory, ): with settings_impacting_urls( ACCOUNT_SIGNUP_FIELDS=["phone*", "password1*"], ACCOUNT_LOGIN_METHODS=("phone",), ACCOUNT_PHONE_VERIFICATION_SUPPORTS_CHANGE=True, ): resp = client.post( headless_reverse("headless:account:signup"), data={ "phone": phone, "password": password_factory(), }, content_type="application/json", ) user = get_user_model().objects.last() assert resp.status_code == HTTPStatus.UNAUTHORIZED assert len(sms_outbox) == 1 new_phone = phone_factory() resp = client.post( headless_reverse("headless:account:manage_phone"), data={ "phone": new_phone, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.ACCEPTED assert resp.json() == { "data": [ { "phone": new_phone, "verified": False, }, ], "status": HTTPStatus.ACCEPTED, } assert len(sms_outbox) == 2 code = sms_outbox[-1]["code"] resp = client.post( headless_reverse("headless:account:verify_phone"), data={ "code": code, }, content_type="application/json", ) assert resp.json()["status"] == HTTPStatus.OK assert get_account_adapter().get_phone(user) == (new_phone, True) @pytest.mark.parametrize("rate_limit_enabled", [(False,), (True,)]) def test_resend_at_signup( db, client, settings_impacting_urls, phone, headless_reverse, headless_client, password_factory, sms_outbox, phone_factory, rate_limit_enabled, request, ): if rate_limit_enabled: request.getfixturevalue("enable_cache") with settings_impacting_urls( ACCOUNT_SIGNUP_FIELDS=["phone*", "password1*"], ACCOUNT_LOGIN_METHODS=("phone",), ACCOUNT_PHONE_VERIFICATION_SUPPORTS_RESEND=True, ): resp = client.post( headless_reverse("headless:account:signup"), data={ "phone": phone, "password": password_factory(), }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED assert len(sms_outbox) == 1 resp = client.post( headless_reverse("headless:account:resend_phone_verification_code"), ) if rate_limit_enabled: assert resp.status_code == HTTPStatus.TOO_MANY_REQUESTS else: assert resp.status_code == HTTPStatus.OK assert len(sms_outbox) == 2 old_code = sms_outbox[0]["code"] new_code = sms_outbox[1]["code"] assert old_code != new_code def test_manage_phone_anon_no_500( client, db, settings_impacting_urls, headless_reverse ): with settings_impacting_urls( ACCOUNT_SIGNUP_FIELDS=["phone*"], ACCOUNT_LOGIN_METHODS=("phone",), ): resp = client.get( headless_reverse("headless:account:manage_phone"), ) assert resp.status_code == HTTPStatus.UNAUTHORIZED ================================================ FILE: tests/apps/headless/account/test_reauthentication.py ================================================ from http import HTTPStatus def test_reauthenticate( auth_client, user, user_password, headless_reverse, headless_client ): resp = auth_client.get( headless_reverse("headless:account:current_session"), content_type="application/json", ) assert resp.status_code == HTTPStatus.OK data = resp.json() method_count = len(data["data"]["methods"]) resp = auth_client.post( headless_reverse("headless:account:reauthenticate"), data={ "password": user_password, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK resp = auth_client.get( headless_reverse("headless:account:current_session"), content_type="application/json", ) assert resp.status_code == HTTPStatus.OK data = resp.json() assert len(data["data"]["methods"]) == method_count + 1 last_method = data["data"]["methods"][-1] assert last_method["method"] == "password" def test_reauthenticate_rate_limit( auth_client, user, user_password, headless_reverse, headless_client, settings, enable_cache, ): settings.ACCOUNT_RATE_LIMITS = {"reauthenticate": "1/m/ip"} for attempt in range(4): resp = auth_client.post( headless_reverse("headless:account:reauthenticate"), data={ "password": user_password, }, content_type="application/json", ) expected_status = HTTPStatus.TOO_MANY_REQUESTS if attempt else HTTPStatus.OK assert resp.status_code == expected_status assert resp.json()["status"] == expected_status ================================================ FILE: tests/apps/headless/account/test_reset_password.py ================================================ from http import HTTPStatus from django.urls import reverse import pytest def test_password_reset_flow( client, user, mailoutbox, password_factory, settings, headless_reverse ): settings.ACCOUNT_EMAIL_NOTIFICATIONS = True resp = client.post( headless_reverse("headless:account:request_password_reset"), data={ "email": user.email, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert len(mailoutbox) == 1 body = mailoutbox[0].body # Extract URL for `password_reset_from_key` view url = body[body.find("/password/reset/") :].split()[0] key = url.split("/")[-2] password = password_factory() # Too simple password resp = client.post( headless_reverse("headless:account:reset_password"), data={ "key": key, "password": "a", }, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "status": HTTPStatus.BAD_REQUEST, "errors": [ { "code": "password_too_short", "message": "This password is too short. It must contain at least 6 characters.", "param": "password", } ], } assert len(mailoutbox) == 1 # Success resp = client.post( headless_reverse("headless:account:reset_password"), data={ "key": key, "password": password, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED user.refresh_from_db() assert user.check_password(password) assert len(mailoutbox) == 2 # The security notification @pytest.mark.parametrize("method", ["get", "post"]) def test_password_reset_flow_wrong_key( client, password_factory, headless_reverse, method ): password = password_factory() if method == "get": resp = client.get( headless_reverse("headless:account:reset_password"), HTTP_X_PASSWORD_RESET_KEY="wrong", ) else: resp = client.post( headless_reverse("headless:account:reset_password"), data={ "key": "wrong", "password": password, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "status": HTTPStatus.BAD_REQUEST, "errors": [ { "param": "key", "code": "invalid_password_reset", "message": "The password reset token was invalid.", } ], } def test_password_reset_flow_unknown_user( client, db, mailoutbox, password_factory, settings, headless_reverse ): resp = client.post( headless_reverse("headless:account:request_password_reset"), data={ "email": "not@registered.org", }, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert len(mailoutbox) == 1 body = mailoutbox[0].body if getattr(settings, "HEADLESS_ONLY", False): assert settings.HEADLESS_FRONTEND_URLS["account_signup"] in body else: assert reverse("account_signup") in body def test_reset_password_rate_limit( auth_client, user, headless_reverse, settings, enable_cache ): settings.ACCOUNT_RATE_LIMITS = {"reset_password": "1/m/ip"} for attempt in range(2): resp = auth_client.post( headless_reverse("headless:account:request_password_reset"), data={"email": user.email}, content_type="application/json", ) expected_status = ( HTTPStatus.OK if attempt == 0 else HTTPStatus.TOO_MANY_REQUESTS ) assert resp.status_code == expected_status assert resp.json()["status"] == expected_status def test_password_reset_key_rate_limit( client, user, settings, headless_reverse, password_reset_key_generator, enable_cache, ): settings.ACCOUNT_RATE_LIMITS = {"reset_password_from_key": "1/m/ip"} for attempt in range(2): resp = client.post( headless_reverse("headless:account:reset_password"), data={ "key": password_reset_key_generator(user), "password": "a", # too short }, content_type="application/json", ) expected_status = ( HTTPStatus.TOO_MANY_REQUESTS if attempt else HTTPStatus.BAD_REQUEST ) assert resp.status_code == expected_status assert resp.json()["status"] == expected_status ================================================ FILE: tests/apps/headless/account/test_reset_password_by_code.py ================================================ from http import HTTPStatus from unittest.mock import ANY import pytest from allauth.account import app_settings from allauth.account.internal.flows.login import AUTHENTICATION_METHODS_SESSION_KEY from allauth.account.models import EmailAddress @pytest.fixture(autouse=True) def prbc_settings(settings_impacting_urls): with settings_impacting_urls(ACCOUNT_PASSWORD_RESET_BY_CODE_ENABLED=True): yield def test_validating_codes_by_get_is_limited(client, user, headless_reverse): resp = client.post( headless_reverse("headless:account:request_password_reset"), data={ "email": user.email, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED for i in range(app_settings.PASSWORD_RESET_BY_CODE_MAX_ATTEMPTS + 1): resp = client.get( headless_reverse("headless:account:reset_password"), HTTP_X_PASSWORD_RESET_KEY=f"attempt{i}", ) if i < app_settings.PASSWORD_RESET_BY_CODE_MAX_ATTEMPTS: expected_status = HTTPStatus.BAD_REQUEST else: expected_status = HTTPStatus.CONFLICT assert resp.status_code == expected_status def test_validating_codes_by_get_limitation_carriers_over_to_post( client, user, headless_reverse ): resp = client.post( headless_reverse("headless:account:request_password_reset"), data={ "email": user.email, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED for i in range(app_settings.PASSWORD_RESET_BY_CODE_MAX_ATTEMPTS): resp = client.get( headless_reverse("headless:account:reset_password"), HTTP_X_PASSWORD_RESET_KEY=f"attempt{i}", ) assert resp.status_code == HTTPStatus.BAD_REQUEST resp = client.post( headless_reverse("headless:account:reset_password"), data={ "key": "wrong", "password": "a", }, content_type="application/json", ) assert resp.status_code == HTTPStatus.CONFLICT @pytest.mark.parametrize("login_on_password_reset", [False, True]) def test_password_reset_flow( client, user, mailoutbox, password_factory, settings, headless_reverse, get_last_password_reset_code, login_on_password_reset, ): settings.ACCOUNT_EMAIL_NOTIFICATIONS = True settings.ACCOUNT_LOGIN_ON_PASSWORD_RESET = login_on_password_reset resp = client.post( headless_reverse("headless:account:request_password_reset"), data={ "email": user.email, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED code = get_last_password_reset_code(client, mailoutbox) password = password_factory() # Get token info resp = client.get( headless_reverse("headless:account:reset_password"), content_type="application/json", HTTP_X_PASSWORD_RESET_KEY=code, ) assert resp.status_code == HTTPStatus.OK # Too simple password resp = client.post( headless_reverse("headless:account:reset_password"), data={ "key": code, "password": "a", }, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "status": HTTPStatus.BAD_REQUEST, "errors": [ { "code": "password_too_short", "message": "This password is too short. It must contain at least 6 characters.", "param": "password", } ], } assert len(mailoutbox) == 1 # Success resp = client.post( headless_reverse("headless:account:reset_password"), data={ "key": code, "password": password, }, content_type="application/json", ) assert ( resp.status_code == HTTPStatus.OK if login_on_password_reset else HTTPStatus.UNAUTHORIZED ) if login_on_password_reset: assert client.headless_session()[AUTHENTICATION_METHODS_SESSION_KEY] == [ {"method": "password_reset", "at": ANY, "email": user.email} ] else: assert AUTHENTICATION_METHODS_SESSION_KEY not in client.headless_session() user.refresh_from_db() assert user.check_password(password) assert len(mailoutbox) == 2 # The security notification # Try once more, shouldn't be allowed. resp = client.post( headless_reverse("headless:account:reset_password"), data={ "key": code, "password": password, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.CONFLICT def test_indirect_email_verification_on_get( db, client, mailoutbox, get_last_password_reset_code, user_factory, headless_reverse ): user = user_factory(email_verified=False) address = EmailAddress.objects.get(user=user, email=user.email, verified=False) resp = client.post( headless_reverse("headless:account:request_password_reset"), data={ "email": address.email, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED resp = client.get( headless_reverse("headless:account:reset_password"), HTTP_X_PASSWORD_RESET_KEY=get_last_password_reset_code(client, mailoutbox), ) assert resp.status_code == HTTPStatus.OK address.refresh_from_db() assert address.verified def test_indirect_email_verification_on_post( db, client, mailoutbox, get_last_password_reset_code, user_factory, headless_reverse, password_factory, ): user = user_factory(email_verified=False) address = EmailAddress.objects.get(user=user, email=user.email, verified=False) resp = client.post( headless_reverse("headless:account:request_password_reset"), data={ "email": address.email, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED resp = client.post( headless_reverse("headless:account:reset_password"), { "key": get_last_password_reset_code(client, mailoutbox), "password": password_factory(), }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED address.refresh_from_db() assert address.verified ================================================ FILE: tests/apps/headless/account/test_session.py ================================================ from http import HTTPStatus from django.test.client import Client from django.urls import reverse def test_app_session_gone(db, user): # intentionally use a vanilla Django test client client = Client() # Force login, creates a Django session client.force_login(user) # That Django session should not play any role. resp = client.get( reverse("headless:app:account:current_session"), HTTP_X_SESSION_TOKEN="gone" ) assert resp.status_code == HTTPStatus.GONE def test_logout(auth_client, headless_reverse): # That Django session should not play any role. resp = auth_client.get(headless_reverse("headless:account:current_session")) assert resp.status_code == HTTPStatus.OK resp = auth_client.delete(headless_reverse("headless:account:current_session")) assert resp.status_code == HTTPStatus.UNAUTHORIZED resp = auth_client.get(headless_reverse("headless:account:current_session")) assert resp.status_code in [HTTPStatus.UNAUTHORIZED, HTTPStatus.GONE] def test_logout_no_token(app_client, user): app_client.force_login(user) resp = app_client.get(reverse("headless:app:account:current_session")) assert resp.status_code == HTTPStatus.OK resp = app_client.delete(reverse("headless:app:account:current_session")) assert resp.status_code == HTTPStatus.UNAUTHORIZED assert "session_token" not in resp.json()["meta"] ================================================ FILE: tests/apps/headless/account/test_signup.py ================================================ from http import HTTPStatus from unittest.mock import ANY, patch from django.contrib.auth.models import User import pytest from allauth.account.models import EmailAddress, EmailConfirmationHMAC from allauth.account.signals import user_logged_in from allauth.headless.base.response import AuthenticationResponse from allauth.headless.constants import Flow def test_signup( db, client, email_factory, password_factory, settings, headless_reverse, headless_client, ): def on_user_logged_in(**kwargs): response = kwargs["response"] assert isinstance(response, AuthenticationResponse) user_logged_in.connect(on_user_logged_in) try: resp = client.post( headless_reverse("headless:account:signup"), data={ "username": "wizard", "email": email_factory(), "password": password_factory(), }, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert User.objects.filter(username="wizard").exists() finally: user_logged_in.disconnect(on_user_logged_in) def test_signup_with_email_verification( db, client, email_factory, password_factory, settings, headless_reverse, headless_client, ): settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.ACCOUNT_USERNAME_REQUIRED = False settings.ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True email = email_factory() resp = client.post( headless_reverse("headless:account:signup"), data={ "email": email, "password": password_factory(), }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED assert User.objects.filter(email=email).exists() data = resp.json() flow = next(f for f in data["data"]["flows"] if f.get("is_pending")) assert flow["id"] == "verify_email" addr = EmailAddress.objects.get(email=email) key = EmailConfirmationHMAC(addr).key resp = client.get( headless_reverse("headless:account:verify_email"), HTTP_X_EMAIL_VERIFICATION_KEY=key, ) assert resp.status_code == HTTPStatus.OK assert resp.json() == { "data": { "email": email, "user": ANY, }, "meta": {"is_authenticating": True}, "status": HTTPStatus.OK, } resp = client.post( headless_reverse("headless:account:verify_email"), data={"key": key}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK data = resp.json() assert data["meta"]["is_authenticated"] addr.refresh_from_db() assert addr.verified @pytest.mark.parametrize("email_verification_by_code_enabled", (False, True)) def test_signup_prevent_enumeration( db, client, email_factory, password_factory, settings, headless_reverse, headless_client, user, mailoutbox, email_verification_by_code_enabled, ): settings.ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = ( email_verification_by_code_enabled ) settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.ACCOUNT_USERNAME_REQUIRED = False settings.ACCOUNT_PREVENT_ENUMERATION = True resp = client.post( headless_reverse("headless:account:signup"), data={ "email": user.email, "password": password_factory(), }, content_type="application/json", ) assert len(mailoutbox) == 1 assert "an account using that email address already exists" in mailoutbox[0].body assert resp.status_code == HTTPStatus.UNAUTHORIZED data = resp.json() assert [f for f in data["data"]["flows"] if f["id"] == Flow.VERIFY_EMAIL][0][ "is_pending" ] resp = client.get(headless_reverse("headless:account:current_session")) data = resp.json() assert [f for f in data["data"]["flows"] if f["id"] == Flow.VERIFY_EMAIL][0][ "is_pending" ] resp = client.post( headless_reverse("headless:account:verify_email"), data={"key": "wrong"}, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST def test_signup_rate_limit( db, client, email_factory, password_factory, settings, headless_reverse, enable_cache, headless_client, ): settings.ACCOUNT_RATE_LIMITS = {"signup": "1/m/ip"} for attempt in range(2): resp = client.post( headless_reverse("headless:account:signup"), data={ "username": f"wizard{attempt}", "email": email_factory(), "password": password_factory(), }, content_type="application/json", ) expected_status = HTTPStatus.TOO_MANY_REQUESTS if attempt else HTTPStatus.OK assert resp.status_code == expected_status assert resp.json()["status"] == expected_status def test_signup_closed( db, client, email_factory, password_factory, settings, headless_reverse, headless_client, ): with patch( "allauth.account.adapter.DefaultAccountAdapter.is_open_for_signup" ) as iofs: iofs.return_value = False resp = client.post( headless_reverse("headless:account:signup"), data={ "username": "wizard", "email": email_factory(), "password": password_factory(), }, content_type="application/json", ) assert resp.status_code == HTTPStatus.FORBIDDEN assert not User.objects.filter(username="wizard").exists() def test_signup_while_logged_in( db, auth_client, email_factory, password_factory, settings, headless_reverse, headless_client, ): resp = auth_client.post( headless_reverse("headless:account:signup"), data={ "username": "wizard", "email": email_factory(), "password": password_factory(), }, content_type="application/json", ) assert resp.status_code == HTTPStatus.CONFLICT assert resp.json() == {"status": HTTPStatus.CONFLICT} def test_signup_without_password( db, client, email_factory, password_factory, headless_reverse, headless_client, settings_impacting_urls, ): with settings_impacting_urls( ACCOUNT_LOGIN_BY_CODE_ENABLED=True, ACCOUNT_EMAIL_VERIFICATION="mandatory", ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED=True, ACCOUNT_SIGNUP_FIELDS=["email*", "password1"], ): email = email_factory() resp = client.post( headless_reverse("headless:account:signup"), data={ "username": "wizard", "email": email, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED user = User.objects.get(email=email) assert not user.check_password("") ================================================ FILE: tests/apps/headless/base/__init__.py ================================================ ================================================ FILE: tests/apps/headless/base/test_views.py ================================================ from http import HTTPStatus def test_config(db, client, headless_reverse): resp = client.get(headless_reverse("headless:config")) assert resp.status_code == HTTPStatus.OK data = resp.json() assert set(data["data"].keys()) == { "account", "mfa", "socialaccount", "usersessions", } ================================================ FILE: tests/apps/headless/conftest.py ================================================ from django.test.client import Client from django.urls import reverse import pytest @pytest.fixture(params=["app", "browser"]) def headless_client(request): return request.param @pytest.fixture def headless_reverse(headless_client): def rev(viewname, **kwargs): viewname = viewname.replace("headless:", f"headless:{headless_client}:") return reverse(viewname, **kwargs) return rev class AppClient(Client): session_token = None def generic(self, *args, **kwargs): if self.session_token: kwargs["HTTP_X_SESSION_TOKEN"] = self.session_token resp = super().generic(*args, **kwargs) if resp["content-type"] == "application/json": data = resp.json() session_token = data.get("meta", {}).get("session_token") if session_token: self.session_token = session_token return resp def force_login(self, user): ret = super().force_login(user) self.session_token = self.session.session_key return ret def headless_session(self): from allauth.headless.internal import sessionkit return sessionkit.session_store(self.session_token) @pytest.fixture def app_client(): return AppClient() @pytest.fixture def client(headless_client): if headless_client == "browser": client = Client() client.headless_session = lambda: client.session return client return AppClient() @pytest.fixture def auth_client(client, user): client.force_login(user) return client ================================================ FILE: tests/apps/headless/contrib/__init__.py ================================================ ================================================ FILE: tests/apps/headless/contrib/ninja/__init__.py ================================================ ================================================ FILE: tests/apps/headless/contrib/ninja/test_security.py ================================================ from allauth.headless.constants import Client from allauth.headless.contrib.ninja.security import XSessionTokenAuth def test_authenticate(rf, user, headless_client, auth_client): if headless_client == Client.BROWSER: return request = rf.get("/", HTTP_X_SESSION_TOKEN=auth_client.session_token) auth_user = XSessionTokenAuth()(request) assert auth_user.pk == user.pk def test_invalid_authentication(rf, user, headless_client, auth_client): if headless_client == Client.BROWSER: return request = rf.get("/", HTTP_X_SESSION_TOKEN="wrong") result = XSessionTokenAuth()(request) assert result is None ================================================ FILE: tests/apps/headless/contrib/rest_framework/__init__.py ================================================ ================================================ FILE: tests/apps/headless/contrib/rest_framework/test_authentication.py ================================================ from allauth.headless.constants import Client from allauth.headless.contrib.rest_framework.authentication import ( XSessionTokenAuthentication, ) def test_authenticate(rf, user, headless_client, auth_client): if headless_client == Client.BROWSER: return request = rf.get("/", HTTP_X_SESSION_TOKEN=auth_client.session_token) auth_user, session = XSessionTokenAuthentication().authenticate(request) assert auth_user.pk == user.pk def test_invalid_authentication(rf, user, headless_client, auth_client): if headless_client == Client.BROWSER: return request = rf.get("/", HTTP_X_SESSION_TOKEN="wrong") result = XSessionTokenAuthentication().authenticate(request) assert result is None ================================================ FILE: tests/apps/headless/internal/__init__.py ================================================ ================================================ FILE: tests/apps/headless/internal/test_authkit.py ================================================ from django.contrib.auth import BACKEND_SESSION_KEY, HASH_SESSION_KEY, SESSION_KEY from django.contrib.auth.middleware import AuthenticationMiddleware from django.contrib.sessions.middleware import SessionMiddleware from django.http import HttpResponse from allauth.headless.internal.authkit import purge_request_user_cache def test_purge_request_user_cache(rf, user): request = rf.get("/") smw = SessionMiddleware(lambda request: HttpResponse()) smw(request) amw = AuthenticationMiddleware(lambda request: HttpResponse()) amw(request) assert request.user.is_anonymous assert not request.user.pk purge_request_user_cache(request) request.session[SESSION_KEY] = user.pk request.session[BACKEND_SESSION_KEY] = ( "allauth.account.auth_backends.AuthenticationBackend" ) request.session[HASH_SESSION_KEY] = user.get_session_auth_hash() assert request.user.is_authenticated assert request.user.pk == user.pk ================================================ FILE: tests/apps/headless/mfa/__init__.py ================================================ ================================================ FILE: tests/apps/headless/mfa/test_recovery_codes.py ================================================ from http import HTTPStatus from allauth.mfa.models import Authenticator def test_get_recovery_codes_requires_reauth( auth_client, user_with_recovery_codes, headless_reverse ): rc = Authenticator.objects.get( type=Authenticator.Type.RECOVERY_CODES, user=user_with_recovery_codes ) resp = auth_client.get(headless_reverse("headless:mfa:manage_recovery_codes")) assert resp.status_code == HTTPStatus.UNAUTHORIZED data = resp.json() assert data["meta"]["is_authenticated"] resp = auth_client.post( headless_reverse("headless:mfa:reauthenticate"), data={"code": rc.wrap().get_unused_codes()[0]}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK def test_get_recovery_codes( auth_client, user_with_recovery_codes, headless_reverse, reauthentication_bypass, ): with reauthentication_bypass(): resp = auth_client.get(headless_reverse("headless:mfa:manage_recovery_codes")) assert resp.status_code == HTTPStatus.OK data = resp.json() assert data["data"]["type"] == "recovery_codes" assert len(data["data"]["unused_codes"]) == 10 with reauthentication_bypass(): resp = auth_client.get(headless_reverse("headless:mfa:authenticators")) data = resp.json() assert len(data["data"]) == 2 rc = [autor for autor in data["data"] if autor["type"] == "recovery_codes"][0] assert "unused_codes" not in rc def test_generate_recovery_codes( auth_client, user_with_totp, headless_reverse, reauthentication_bypass, ): with reauthentication_bypass(): resp = auth_client.get(headless_reverse("headless:mfa:manage_recovery_codes")) assert resp.status_code == HTTPStatus.NOT_FOUND with reauthentication_bypass(): resp = auth_client.post( headless_reverse("headless:mfa:manage_recovery_codes"), content_type="application/json", ) assert resp.status_code == HTTPStatus.OK data = resp.json() assert data["data"]["type"] == "recovery_codes" assert len(data["data"]["unused_codes"]) == 10 ================================================ FILE: tests/apps/headless/mfa/test_totp.py ================================================ from http import HTTPStatus import pytest from allauth.account.internal.flows.login import AUTHENTICATION_METHODS_SESSION_KEY from allauth.mfa.models import Authenticator @pytest.mark.parametrize("email_verified", [False, True]) def test_get_totp_not_active(auth_client, user, headless_reverse, email_verified): resp = auth_client.get(headless_reverse("headless:mfa:manage_totp")) if email_verified: assert resp.status_code == HTTPStatus.NOT_FOUND data = resp.json() assert len(data["meta"]["secret"]) == 32 assert len(data["meta"]["totp_url"]) == 145 else: assert resp.status_code == HTTPStatus.CONFLICT assert resp.json() == { "status": HTTPStatus.CONFLICT, "errors": [ { "message": "You cannot activate two-factor authentication until you have verified your email address.", "code": "unverified_email", } ], } def test_get_totp( auth_client, user_with_totp, headless_reverse, ): resp = auth_client.get(headless_reverse("headless:mfa:manage_totp")) assert resp.status_code == HTTPStatus.OK data = resp.json() assert data["data"]["type"] == "totp" assert isinstance(data["data"]["created_at"], float) def test_deactivate_totp( auth_client, user_with_totp, headless_reverse, reauthentication_bypass, ): with reauthentication_bypass(): resp = auth_client.delete(headless_reverse("headless:mfa:manage_totp")) assert resp.status_code == HTTPStatus.OK assert not Authenticator.objects.filter(user=user_with_totp).exists() def test_deactivate_totp_with_reauthenticate( auth_client, user_with_totp, headless_reverse, totp_validation_bypass, ): # Let's ensure the session require reauthentication by having an old `at`. session = auth_client.headless_session() session[AUTHENTICATION_METHODS_SESSION_KEY] = [{"at": 0}] session.save() # Attempt to deactivate, should not be allowed. resp = auth_client.delete(headless_reverse("headless:mfa:manage_totp")) assert resp.status_code == HTTPStatus.UNAUTHORIZED assert resp.json()["data"]["flows"] == [ {"id": "reauthenticate"}, {"id": "mfa_reauthenticate", "types": ["totp"]}, ] assert Authenticator.objects.filter(user=user_with_totp).exists() # Now, reauthenticate... with totp_validation_bypass(): resp = auth_client.post( headless_reverse("headless:mfa:reauthenticate"), data={"code": "42"}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK # ... and try again. resp = auth_client.delete(headless_reverse("headless:mfa:manage_totp")) assert resp.status_code == HTTPStatus.OK # Success assert not Authenticator.objects.filter(user=user_with_totp).exists() @pytest.mark.parametrize("email_verified", [False, True]) def test_activate_totp( auth_client, user, headless_reverse, reauthentication_bypass, settings, totp_validation_bypass, email_verified, ): with reauthentication_bypass(): with totp_validation_bypass(): resp = auth_client.post( headless_reverse("headless:mfa:manage_totp"), data={"code": "42"}, content_type="application/json", ) if email_verified: assert resp.status_code == HTTPStatus.OK assert Authenticator.objects.filter( user=user, type=Authenticator.Type.TOTP ).exists() data = resp.json() assert data["data"]["type"] == "totp" assert isinstance(data["data"]["created_at"], float) assert data["data"]["last_used_at"] is None else: assert resp.status_code == HTTPStatus.BAD_REQUEST ================================================ FILE: tests/apps/headless/mfa/test_trust.py ================================================ from http import HTTPStatus import pytest from allauth.headless.constants import Client, Flow @pytest.mark.parametrize("trust", [False, True]) def test_auth_unverified_email_and_mfa( client, user_with_totp, user_password, settings_impacting_urls, totp_validation_bypass, headless_reverse, headless_client, trust, ): with settings_impacting_urls( ACCOUNT_LOGIN_METHODS={"email"}, ACCOUNT_EMAIL_VERIFICATION="mandatory", ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION=True, MFA_TRUST_ENABLED=True, ): # Login resp = client.post( headless_reverse("headless:account:login"), data={ "email": user_with_totp.email, "password": user_password, }, content_type="application/json", ) # TOTP is the next stage assert resp.status_code == HTTPStatus.UNAUTHORIZED data = resp.json() assert [f for f in data["data"]["flows"] if f["id"] == Flow.MFA_AUTHENTICATE][ 0 ]["is_pending"] with totp_validation_bypass(): resp = client.post( headless_reverse("headless:mfa:authenticate"), data={"code": "bad"}, content_type="application/json", ) if headless_client == Client.APP: # App client does not support trust assert resp.status_code == HTTPStatus.OK else: # Trust stage is pending assert resp.status_code == HTTPStatus.UNAUTHORIZED data = resp.json() assert [f for f in data["data"]["flows"] if f["id"] == Flow.MFA_TRUST][0][ "is_pending" ] # Indicate trust Y/N resp = client.post( headless_reverse("headless:mfa:trust"), data={"trust": trust}, content_type="application/json", ) # Logout assert resp.status_code == HTTPStatus.OK resp = client.delete( headless_reverse("headless:account:current_session"), ) assert resp.status_code == HTTPStatus.UNAUTHORIZED # Login resp = client.post( headless_reverse("headless:account:login"), data={ "email": user_with_totp.email, "password": user_password, }, content_type="application/json", ) # Trust used? assert resp.status_code == ( HTTPStatus.OK if trust else HTTPStatus.UNAUTHORIZED ) ================================================ FILE: tests/apps/headless/mfa/test_views.py ================================================ from http import HTTPStatus from allauth.account.models import EmailAddress, get_emailconfirmation_model from allauth.headless.constants import Flow def test_auth_unverified_email_and_mfa( client, user_factory, password_factory, settings, totp_validation_bypass, headless_reverse, headless_client, ): settings.ACCOUNT_LOGIN_METHODS = {"email"} settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True password = password_factory() user = user_factory(email_verified=False, password=password, with_totp=True) resp = client.post( headless_reverse("headless:account:login"), data={ "email": user.email, "password": password, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED data = resp.json() assert [f for f in data["data"]["flows"] if f["id"] == Flow.VERIFY_EMAIL][0][ "is_pending" ] emailaddress = EmailAddress.objects.filter(user=user, verified=False).get() key = get_emailconfirmation_model().create(emailaddress).key resp = client.post( headless_reverse("headless:account:verify_email"), data={"key": key}, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED flows = [ {"id": "login"}, {"id": "login_by_code"}, {"id": "signup"}, ] if headless_client == "browser": flows.append( { "id": "provider_redirect", "providers": ["dummy", "unittest-server", "other-server"], } ) flows.append( { "id": "provider_token", "providers": ["dummy", "unittest-server", "other-server"], } ) flows.append({"id": "mfa_login_webauthn"}) flows.append( { "id": "mfa_authenticate", "is_pending": True, "types": ["totp"], } ) assert resp.json() == { "data": {"flows": flows}, "meta": {"is_authenticated": False}, "status": HTTPStatus.UNAUTHORIZED, } resp = client.post( headless_reverse("headless:mfa:authenticate"), data={"code": "bad"}, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "status": HTTPStatus.BAD_REQUEST, "errors": [ {"message": "Incorrect code.", "code": "incorrect_code", "param": "code"} ], } with totp_validation_bypass(): resp = client.post( headless_reverse("headless:mfa:authenticate"), data={"code": "bad"}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK def test_dangling_mfa_is_logged_out( client, user_with_totp, password_factory, settings, totp_validation_bypass, headless_reverse, headless_client, user_password, ): settings.ACCOUNT_LOGIN_METHODS = {"email"} resp = client.post( headless_reverse("headless:account:login"), data={ "email": user_with_totp.email, "password": user_password, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED data = resp.json() flow = [f for f in data["data"]["flows"] if f["id"] == Flow.MFA_AUTHENTICATE][0] assert flow["is_pending"] assert flow["types"] == ["totp"] resp = client.delete(headless_reverse("headless:account:current_session")) data = resp.json() assert resp.status_code == HTTPStatus.UNAUTHORIZED assert all(not f.get("is_pending") for f in data["data"]["flows"]) ================================================ FILE: tests/apps/headless/mfa/test_webauthn.py ================================================ from http import HTTPStatus from unittest.mock import ANY from django.contrib.auth import get_user_model import pytest from allauth.account import app_settings from allauth.account.authentication import AUTHENTICATION_METHODS_SESSION_KEY from allauth.headless.constants import Flow from allauth.mfa.models import Authenticator def test_passkey_login( client, passkey, webauthn_authentication_bypass, headless_reverse ): with webauthn_authentication_bypass(passkey) as credential: resp = client.get(headless_reverse("headless:mfa:login_webauthn")) assert "request_options" in resp.json()["data"] resp = client.post( headless_reverse("headless:mfa:login_webauthn"), data={"credential": credential}, content_type="application/json", ) data = resp.json() assert data["data"]["user"]["id"] == passkey.user_id def test_passkey_login_get_options(client, headless_client, headless_reverse, db): resp = client.get(headless_reverse("headless:mfa:login_webauthn")) data = resp.json() meta = {} if headless_client == "app": meta = { "meta": {"session_token": ANY}, } assert data == { "status": HTTPStatus.OK, "data": {"request_options": {"publicKey": ANY}}, **meta, } def test_reauthenticate( auth_client, passkey, user_with_recovery_codes, webauthn_authentication_bypass, headless_reverse, ): # View recovery codes, confirm webauthn reauthentication is an option resp = auth_client.get(headless_reverse("headless:mfa:manage_recovery_codes")) assert resp.status_code == HTTPStatus.UNAUTHORIZED assert Flow.MFA_REAUTHENTICATE in [ flow["id"] for flow in resp.json()["data"]["flows"] ] # Get request options with webauthn_authentication_bypass(passkey): resp = auth_client.get(headless_reverse("headless:mfa:reauthenticate_webauthn")) data = resp.json() assert data["status"] == HTTPStatus.OK assert data["data"]["request_options"] == ANY # Reauthenticate with webauthn_authentication_bypass(passkey) as credential: resp = auth_client.post( headless_reverse("headless:mfa:reauthenticate_webauthn"), data={"credential": credential}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK resp = auth_client.get(headless_reverse("headless:mfa:manage_recovery_codes")) assert resp.status_code == HTTPStatus.OK def test_update_authenticator( auth_client, headless_reverse, passkey, reauthentication_bypass ): data = {"id": passkey.pk, "name": "Renamed!"} resp = auth_client.put( headless_reverse("headless:mfa:manage_webauthn"), data=data, content_type="application/json", ) # Reauthentication required assert resp.status_code == HTTPStatus.UNAUTHORIZED with reauthentication_bypass(): resp = auth_client.put( headless_reverse("headless:mfa:manage_webauthn"), data=data, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK passkey.refresh_from_db() assert passkey.wrap().name == "Renamed!" def test_delete_authenticator( auth_client, headless_reverse, passkey, reauthentication_bypass ): data = {"authenticators": [passkey.pk]} resp = auth_client.delete( headless_reverse("headless:mfa:manage_webauthn"), data=data, content_type="application/json", ) # Reauthentication required assert resp.status_code == HTTPStatus.UNAUTHORIZED with reauthentication_bypass(): resp = auth_client.delete( headless_reverse("headless:mfa:manage_webauthn"), data=data, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert not Authenticator.objects.filter(pk=passkey.pk).exists() @pytest.mark.parametrize("email_verified", [False, True]) def test_add_authenticator( user, auth_client, headless_reverse, webauthn_registration_bypass, reauthentication_bypass, email_verified, ): resp = auth_client.get(headless_reverse("headless:mfa:manage_webauthn")) # Reauthentication required assert ( resp.status_code == HTTPStatus.UNAUTHORIZED if email_verified else HTTPStatus.CONFLICT ) with reauthentication_bypass(): resp = auth_client.get(headless_reverse("headless:mfa:manage_webauthn")) if email_verified: assert resp.status_code == HTTPStatus.OK data = resp.json() assert data["data"]["creation_options"] == ANY else: assert resp.status_code == HTTPStatus.CONFLICT with webauthn_registration_bypass(user, False) as credential: resp = auth_client.post( headless_reverse("headless:mfa:manage_webauthn"), data={"credential": credential}, content_type="application/json", ) webauthn_count = Authenticator.objects.filter( type=Authenticator.Type.WEBAUTHN, user=user ).count() if email_verified: assert resp.status_code == HTTPStatus.OK assert webauthn_count == 1 else: assert resp.status_code == HTTPStatus.CONFLICT assert webauthn_count == 0 def test_2fa_login( client, user, user_password, passkey, webauthn_authentication_bypass, headless_reverse, ): resp = client.post( headless_reverse("headless:account:login"), data={ "username": user.username, "password": user_password, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED data = resp.json() pending_flows = [f for f in data["data"]["flows"] if f.get("is_pending")] assert len(pending_flows) == 1 pending_flow = pending_flows[0] assert pending_flow == { "id": "mfa_authenticate", "is_pending": True, "types": ["webauthn"], } with webauthn_authentication_bypass(passkey) as credential: resp = client.get(headless_reverse("headless:mfa:authenticate_webauthn")) assert "request_options" in resp.json()["data"] resp = client.post( headless_reverse("headless:mfa:authenticate_webauthn"), data={"credential": credential}, content_type="application/json", ) data = resp.json() assert resp.status_code == HTTPStatus.OK assert data["data"]["user"]["id"] == passkey.user_id assert client.headless_session()[AUTHENTICATION_METHODS_SESSION_KEY] == [ {"method": "password", "at": ANY, "username": passkey.user.username}, {"method": "mfa", "at": ANY, "id": ANY, "type": Authenticator.Type.WEBAUTHN}, ] @pytest.mark.parametrize("login_on_email_verification", [False, True]) def test_passkey_signup( client, db, webauthn_registration_bypass, headless_reverse, settings, get_last_email_verification_code, mailoutbox, login_on_email_verification, ): settings.ACCOUNT_EMAIL_VERIFICATION = app_settings.EmailVerificationMethod.MANDATORY settings.ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True # This setting should have no influence when verifying by code: settings.ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = login_on_email_verification # Initiate passkey signup resp = client.post( headless_reverse("headless:mfa:signup_webauthn"), data={"email": "pass@key.org", "username": "passkey"}, content_type="application/json", ) # Email verification kicks in. assert resp.status_code == HTTPStatus.UNAUTHORIZED pending_flows = [ flow for flow in resp.json()["data"]["flows"] if flow.get("is_pending") ] assert len(pending_flows) == 1 flow = pending_flows[0] assert flow["id"] == Flow.VERIFY_EMAIL.value # Verify email. code = get_last_email_verification_code(client, mailoutbox) resp = client.post( headless_reverse("headless:account:verify_email"), data={"key": code}, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED # Now, the webauthn signup flow is pending. pending_flows = [ flow for flow in resp.json()["data"]["flows"] if flow.get("is_pending") ] assert len(pending_flows) == 1 flow = pending_flows[0] assert flow["id"] == Flow.MFA_SIGNUP_WEBAUTHN.value # Fetch flow creation options. resp = client.get(headless_reverse("headless:mfa:signup_webauthn")) data = resp.json() assert "creation_options" in data["data"] # Create a passkey. user = get_user_model().objects.get(email="pass@key.org") with webauthn_registration_bypass(user, True) as credential: resp = client.put( headless_reverse("headless:mfa:signup_webauthn"), data={"name": "Some key", "credential": credential}, content_type="application/json", ) # Signed up successfully. data = resp.json() assert resp.status_code == HTTPStatus.OK assert data["meta"]["is_authenticated"] authenticator = Authenticator.objects.get(user=user) assert authenticator.wrap().name == "Some key" def test_get_login_webauthn_doesnt_crash_with_jwt( db, client, headless_reverse, settings ): settings.HEADLESS_TOKEN_STRATEGY = ( "tests.apps.headless.tokens.test_jwttokenstrategy.CustomJWTTokenStrategy" ) resp = client.get(headless_reverse("headless:mfa:login_webauthn")) assert resp.status_code == HTTPStatus.OK ================================================ FILE: tests/apps/headless/socialaccount/__init__.py ================================================ ================================================ FILE: tests/apps/headless/socialaccount/test_inputs.py ================================================ import pytest from allauth.headless.socialaccount.inputs import ProviderTokenInput @pytest.mark.parametrize("client_id", ["client1", "client2"]) def test_provider_token_multiple_apps(settings, db, client_id): gsettings = { "APPS": [ {"client_id": "client1", "secret": "secret"}, {"client_id": "client2", "secret": "secret"}, ] } settings.SOCIALACCOUNT_PROVIDERS = {"google": gsettings} inp = ProviderTokenInput( { "provider": "google", "process": "login", "token": {"client_id": client_id, "id_token": "it", "access_token": "at"}, } ) assert not inp.is_valid() assert inp.cleaned_data["provider"].app.client_id == client_id assert inp.errors == {"token": ["Invalid token."]} def test_provider_token_client_id_required(settings, db): inp = ProviderTokenInput( { "provider": "google", "process": "login", "token": {"id_token": "it", "access_token": "at"}, } ) assert not inp.is_valid() assert inp.errors == {"token": ["`client_id` required."]} ================================================ FILE: tests/apps/headless/socialaccount/test_views.py ================================================ import json from http import HTTPStatus from unittest.mock import patch from django.urls import reverse from pytest_django.asserts import assertTemplateUsed from allauth.account.models import EmailAddress from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.base.constants import AuthProcess def test_bad_redirect(client, headless_reverse, db, settings): settings.HEADLESS_ONLY = False resp = client.post( headless_reverse("headless:socialaccount:redirect_to_provider"), data={ "provider": "dummy", "callback_url": "https://unsafe.org/hack", "process": AuthProcess.LOGIN, }, ) assertTemplateUsed(resp, "socialaccount/authentication_error.html") def test_valid_redirect(client, headless_reverse, db): resp = client.post( headless_reverse("headless:socialaccount:redirect_to_provider"), data={ "provider": "dummy", "callback_url": "/", "process": AuthProcess.LOGIN, }, ) assert resp.status_code == HTTPStatus.FOUND def test_unknown_provider_redirect(client, headless_reverse, db, settings): resp = client.post( headless_reverse("headless:socialaccount:redirect_to_provider"), data={ "provider": "unknown", "callback_url": "/", "process": AuthProcess.LOGIN, }, ) if getattr(settings, "HEADLESS_ONLY", False): assert resp.status_code == HTTPStatus.FOUND else: assert resp.status_code == HTTPStatus.UNAUTHORIZED def test_manage_providers(auth_client, user, headless_reverse, provider_id): account_to_del = SocialAccount.objects.create( user=user, provider=provider_id, uid="p123" ) account_to_keep = SocialAccount.objects.create( user=user, provider=provider_id, uid="p456" ) resp = auth_client.get( headless_reverse("headless:socialaccount:manage_providers"), ) data = resp.json() assert data["status"] == HTTPStatus.OK assert len(data["data"]) == 2 resp = auth_client.delete( headless_reverse("headless:socialaccount:manage_providers"), data={"provider": account_to_del.provider, "account": account_to_del.uid}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert resp.json() == { "status": HTTPStatus.OK, "data": [ { "display": "Unittest Server", "provider": { "client_id": "Unittest client_id", "openid_configuration_url": "https://unittest.example.com/.well-known/openid-configuration", "flows": ["provider_redirect", "provider_token"], "id": provider_id, "name": "Unittest Server", }, "uid": "p456", } ], } assert not SocialAccount.objects.filter(pk=account_to_del.pk).exists() assert SocialAccount.objects.filter(pk=account_to_keep.pk).exists() def test_disconnect_bad_request(auth_client, user, headless_reverse, provider_id): resp = auth_client.delete( headless_reverse("headless:socialaccount:manage_providers"), data={"provider": provider_id, "account": "unknown"}, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "status": HTTPStatus.BAD_REQUEST, "errors": [{"code": "account_not_found", "message": "Unknown account."}], } def test_disconnect_not_allowed(auth_client, user, headless_reverse, provider_id): user.set_unusable_password() user.save(update_fields=["password"]) auth_client.force_login(user) account = SocialAccount.objects.create(user=user, uid="123", provider=provider_id) resp = auth_client.delete( headless_reverse("headless:socialaccount:manage_providers"), data={"provider": provider_id, "account": account.uid}, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "status": HTTPStatus.BAD_REQUEST, "errors": [ {"code": "no_password", "message": "Your account has no password set up."} ], } def test_valid_token(client, headless_reverse, db): id_token = json.dumps( { "id": 123, "email": "a@b.com", "email_verified": True, } ) resp = client.post( headless_reverse("headless:socialaccount:provider_token"), data={ "provider": "dummy", "token": { "id_token": id_token, }, "process": AuthProcess.LOGIN, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert EmailAddress.objects.filter(email="a@b.com", verified=True).exists() def test_invalid_token(client, headless_reverse, db, google_provider_settings): resp = client.post( headless_reverse("headless:socialaccount:provider_token"), data={ "provider": "google", "token": { "id_token": "dummy", "client_id": google_provider_settings["APPS"][0]["client_id"], }, "process": AuthProcess.LOGIN, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST data = resp.json() assert data == { "status": HTTPStatus.BAD_REQUEST, "errors": [ {"message": "Invalid token.", "code": "invalid_token", "param": "token"} ], } def test_valid_token_multiple_apps( client, headless_reverse, db, google_provider_settings, settings, user_factory ): settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" google_provider_settings["APPS"].append( {"client_id": "client_id2", "secret": "secret2"} ) id_token = {"sub": "uid-from-id-token", "email": "a@b.com", "email_verified": True} with patch( "allauth.socialaccount.providers.google.views._verify_and_decode", return_value=id_token, ): resp = client.post( headless_reverse("headless:socialaccount:provider_token"), data={ "provider": "google", "token": { "id_token": "dummy", "client_id": google_provider_settings["APPS"][0]["client_id"], }, "process": AuthProcess.LOGIN, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK def test_auth_error_no_headless_request(client, db, google_provider_settings, settings): """Authentication errors use the regular "Third-Party Login Failure" template if headless is not used. """ settings.HEADLESS_ONLY = False resp = client.get(reverse("google_callback")) assertTemplateUsed(resp, "socialaccount/authentication_error.html") def test_auth_error_headless_request( client, db, google_provider_settings, sociallogin_setup_state ): """Authentication errors redirect to the next URL with ?error params for headless requests. """ state = sociallogin_setup_state(client, headless=True, next="/foo") resp = client.get(f"{reverse('google_callback')}?state={state}") assert resp["location"] == "/foo?error=unknown&error_process=login" def test_auth_error_no_headless_state_request_headless_only( settings, client, db, google_provider_settings ): """Authentication errors redirect to a fallback error URL for headless-only, in case no next can be recovered from the state. """ settings.HEADLESS_ONLY = True settings.HEADLESS_FRONTEND_URLS = {"socialaccount_login_error": "/3rdparty/failure"} resp = client.get(reverse("google_callback")) assert ( resp["location"] == "http://testserver/3rdparty/failure?error=unknown&error_process=login" ) def test_auth_error_headless_state_request_headless_only( settings, client, db, google_provider_settings, sociallogin_setup_state ): """Authentication errors redirect to a fallback error URL for headless-only, in case no next can be recovered from the state. """ state = sociallogin_setup_state(client, headless=True, next="/foo") settings.HEADLESS_ONLY = True settings.HEADLESS_FRONTEND_URLS = {"socialaccount_login_error": "/3rdparty/failure"} resp = client.get(f"{reverse('google_callback')}?state={state}") assert resp["location"] == "/foo?error=unknown&error_process=login" def test_token_signup_closed(client, headless_reverse, db): id_token = json.dumps( { "id": 123, "email": "a@b.com", "email_verified": True, } ) with patch( "allauth.socialaccount.adapter.DefaultSocialAccountAdapter.is_open_for_signup" ) as iofs: iofs.return_value = False resp = client.post( headless_reverse("headless:socialaccount:provider_token"), data={ "provider": "dummy", "token": { "id_token": id_token, }, "process": AuthProcess.LOGIN, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.FORBIDDEN assert not EmailAddress.objects.filter(email="a@b.com", verified=True).exists() def test_provider_signup(client, headless_reverse, db, settings): settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.ACCOUNT_EMAIL_REQUIRED = True settings.ACCOUNT_USERNAME_REQUIRED = False account_uid = "123" id_token = json.dumps( { "id": account_uid, } ) resp = client.post( headless_reverse("headless:socialaccount:provider_token"), data={ "provider": "dummy", "token": { "id_token": id_token, }, "process": AuthProcess.LOGIN, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED pending_flow = [f for f in resp.json()["data"]["flows"] if f.get("is_pending")][0] assert pending_flow["id"] == "provider_signup" resp = client.get(headless_reverse("headless:socialaccount:provider_signup")) assert resp.status_code == HTTPStatus.OK assert resp.json() == { "data": { "email": [], "account": { "display": "Dummy", "provider": { "flows": ["provider_redirect", "provider_token"], "id": "dummy", "name": "Dummy", }, "uid": account_uid, }, "user": {"display": "user", "has_usable_password": False}, }, "status": HTTPStatus.OK, } resp = client.post( headless_reverse("headless:socialaccount:provider_signup"), data={ "email": "a@b.com", }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED pending_flow = [f for f in resp.json()["data"]["flows"] if f.get("is_pending")][0] assert pending_flow["id"] == "verify_email" assert EmailAddress.objects.filter(email="a@b.com").exists() def test_signup_closed(client, headless_reverse, db, settings): settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.ACCOUNT_EMAIL_REQUIRED = True settings.ACCOUNT_USERNAME_REQUIRED = False id_token = json.dumps( { "id": 123, } ) resp = client.post( headless_reverse("headless:socialaccount:provider_token"), data={ "provider": "dummy", "token": { "id_token": id_token, }, "process": AuthProcess.LOGIN, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED pending_flow = [f for f in resp.json()["data"]["flows"] if f.get("is_pending")][0] assert pending_flow["id"] == "provider_signup" with patch( "allauth.socialaccount.adapter.DefaultSocialAccountAdapter.is_open_for_signup" ) as iofs: iofs.return_value = False resp = client.post( headless_reverse("headless:socialaccount:provider_signup"), data={ "email": "a@b.com", }, content_type="application/json", ) assert resp.status_code == HTTPStatus.FORBIDDEN def test_connect(user, auth_client, sociallogin_setup_state, headless_reverse, db): state = sociallogin_setup_state( auth_client, process="connect", next="/foo", headless=True ) resp = auth_client.post( f"{reverse('dummy_authenticate')}?state={state}", data={ "id": 123, }, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == "/foo" assert SocialAccount.objects.filter(user=user, provider="dummy", uid="123").exists() def test_connect_reauthentication_required( user, auth_client, sociallogin_setup_state, headless_reverse, db, settings ): settings.ACCOUNT_REAUTHENTICATION_REQUIRED = True state = sociallogin_setup_state( auth_client, process="connect", next="/foo", headless=True ) resp = auth_client.post( f"{reverse('dummy_authenticate')}?state={state}", data={ "id": 123, }, ) assert resp.status_code == HTTPStatus.FOUND assert ( resp["location"] == "/foo?error=reauthentication_required&error_process=connect" ) def test_connect_already_connected( user, user_factory, auth_client, sociallogin_setup_state, headless_reverse, db ): # The other user already connected the account. other_user = user_factory() SocialAccount.objects.create(user=other_user, uid="123", provider="dummy") # Then, this user tries to connect... state = sociallogin_setup_state( auth_client, process=AuthProcess.CONNECT, next="/foo", headless=True ) resp = auth_client.post( f"{reverse('dummy_authenticate')}?state={state}", data={ "id": 123, }, ) # We're redirected, and an error code is shown. assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == "/foo?error=connected_other&error_process=connect" assert not SocialAccount.objects.filter( user=user, provider="dummy", uid="123" ).exists() def test_token_connect(user, auth_client, headless_reverse, db): id_token = json.dumps( { "id": 123, "email": "a@b.com", "email_verified": True, } ) resp = auth_client.post( headless_reverse("headless:socialaccount:provider_token"), data={ "provider": "dummy", "token": { "id_token": id_token, }, "process": AuthProcess.CONNECT, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert SocialAccount.objects.filter(uid="123", user=user).exists() def test_token_connect_already_connected( user, auth_client, headless_reverse, db, user_factory ): # The other user already connected the account. other_user = user_factory() SocialAccount.objects.create(user=other_user, uid="123", provider="dummy") id_token = json.dumps( { "id": 123, "email": "a@b.com", "email_verified": True, } ) resp = auth_client.post( headless_reverse("headless:socialaccount:provider_token"), data={ "provider": "dummy", "token": { "id_token": id_token, }, "process": AuthProcess.CONNECT, }, content_type="application/json", ) assert not SocialAccount.objects.filter(uid="123", user=user).exists() assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "status": HTTPStatus.BAD_REQUEST, "errors": [ { "code": "connected_other", "message": "The third-party account is already connected to a different account.", } ], } def test_provider_signup_not_pending(client, headless_reverse, db, settings): resp = client.post( headless_reverse("headless:socialaccount:provider_signup"), data={ "email": "a@b.com", }, content_type="application/json", ) assert resp.status_code == HTTPStatus.CONFLICT def test_valid_openid_connect_token( client, headless_reverse, db, openid_connect_provider_id ): provider = get_adapter().get_provider(None, openid_connect_provider_id) id_token = json.dumps( { "id": 123, "email": "a@b.com", "email_verified": True, } ) with patch( "allauth.socialaccount.providers.openid_connect.views.OpenIDConnectOAuth2Adapter.openid_config" ): with patch("allauth.socialaccount.internal.jwtkit.verify_and_decode") as vad: vad.return_value = {"sub": "123"} resp = client.post( headless_reverse("headless:socialaccount:provider_token"), data={ "provider": openid_connect_provider_id, "token": { "client_id": provider.app.client_id, "id_token": id_token, }, "process": AuthProcess.LOGIN, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK assert SocialAccount.objects.filter(uid="123").exists() ================================================ FILE: tests/apps/headless/spec/__init__.py ================================================ ================================================ FILE: tests/apps/headless/spec/internal/__init__.py ================================================ ================================================ FILE: tests/apps/headless/spec/internal/test_openapikit.py ================================================ from dataclasses import dataclass, field from typing import Optional from allauth.headless.spec.internal.openapikit import spec_for_dataclass @dataclass class NestedDataClass: string: str integer: int = field( metadata={ "description": "Some nested int", "example": 42, } ) @dataclass class ExampleDataclass: optional_integer: Optional[int] integer: int optional_string: Optional[str] string: str number: float = field( metadata={ "description": "Some float", "example": "3.14", } ) nested: Optional[NestedDataClass] def test_spec_for_dataclass(): spec = spec_for_dataclass(ExampleDataclass) assert spec == ( { "properties": { "integer": { "type": "integer", }, "number": { "description": "Some float", "example": "3.14", "format": "float", "type": "number", }, "optional_integer": { "type": "integer", }, "optional_string": { "type": "string", }, "string": { "type": "string", }, "nested": { "example": {"integer": 42}, "properties": { "string": { "type": "string", }, "integer": { "description": "Some nested int", "example": 42, "type": "integer", }, }, "required": [ "string", "integer", ], "type": "object", }, }, "required": [ "integer", "string", "number", ], "type": "object", }, { "number": "3.14", }, ) ================================================ FILE: tests/apps/headless/spec/test_views.py ================================================ from http import HTTPStatus from django.urls import reverse def test_openapi_json(client): resp = client.get(reverse("headless:openapi_json")) assert resp.status_code == HTTPStatus.OK data = resp.json() assert data["openapi"] == "3.0.3" assert data["info"]["description"].startswith("# Introduction") ================================================ FILE: tests/apps/headless/tokens/test_jwttokenstrategy.py ================================================ from http import HTTPStatus from django.test.client import Client from django.urls import reverse, reverse_lazy import jwt import pytest from allauth.headless.tokens.strategies.jwt import JWTTokenStrategy class CustomJWTTokenStrategy(JWTTokenStrategy): def get_claims(self, user): return {"email": user.email} @pytest.fixture def custom_jwt_token_strategy(settings): settings.HEADLESS_TOKEN_STRATEGY = ( "tests.apps.headless.tokens.test_jwttokenstrategy.CustomJWTTokenStrategy" ) @pytest.fixture def obtain_tokens(user, user_password, headless_reverse): def f(client): # Let's sign in resp = client.post( headless_reverse("headless:account:login"), data={ "username": user.username, "password": user_password, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK # On sign in, we receive an access/refresh token pair. meta = resp.json()["meta"] assert meta["is_authenticated"] access_token = meta.get("access_token") refresh_token = meta.get("refresh_token") return access_token, refresh_token return f @pytest.mark.parametrize("rotate", [False, True]) def test_rotate_refresh_token( headless_client, headless_reverse, client, user, rotate, settings, obtain_tokens, custom_jwt_token_strategy, ): if headless_client == "browser": return settings.HEADLESS_JWT_ROTATE_REFRESH_TOKEN = rotate access_token, refresh_token = obtain_tokens(client) # Check our custom claim access_token_data = jwt.decode( access_token, algorithms=["RS256"], options={"verify_signature": False} ) assert access_token_data["email"] == user.email # Let's refresh resp = Client().post( headless_reverse("headless:tokens:refresh"), data={"refresh_token": refresh_token}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK data = resp.json()["data"] # Check our custom claim in the new access token access_token = data["access_token"] access_token_data = jwt.decode( access_token, algorithms=["RS256"], options={"verify_signature": False} ) assert access_token_data["email"] == user.email new_refresh_token = data.get("refresh_token") assert bool(new_refresh_token) == rotate # Let's refresh, again using the previous refresh token resp = Client().post( headless_reverse("headless:tokens:refresh"), data={"refresh_token": refresh_token}, content_type="application/json", ) assert resp.status_code == (HTTPStatus.BAD_REQUEST if rotate else HTTPStatus.OK) # Let's refresh, using the new refresh token if new_refresh_token: resp = Client().post( headless_reverse("headless:tokens:refresh"), data={"refresh_token": new_refresh_token}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK @pytest.mark.parametrize("stateful_validation_enabled", [False, True]) def test_flow( headless_client, headless_reverse, client, user, settings, stateful_validation_enabled, obtain_tokens, ): settings.HEADLESS_JWT_ROTATE_REFRESH_TOKEN = False settings.HEADLESS_TOKEN_STRATEGY = ( "allauth.headless.tokens.strategies.jwt.JWTTokenStrategy" ) settings.HEADLESS_JWT_STATEFUL_VALIDATION_ENABLED = stateful_validation_enabled access_token, refresh_token = obtain_tokens(client) # On sign in, we receive an access/refresh token pair. if headless_client == "browser": assert not access_token assert not refresh_token return # With the access token, we can reach out to the allauth API. at_client = Client(HTTP_AUTHORIZATION=f"Bearer {access_token}") resp = at_client.get(headless_reverse("headless:account:current_session")) assert resp.status_code == HTTPStatus.OK # Also check validity with DRF & Ninja endpoints for url in [ reverse("headless_rest_framework_resource"), "/headless/ninja/resource", ]: resp = Client(HTTP_AUTHORIZATION=f"Bearer {access_token}").get( f"{url}?userinfo" ) assert resp.status_code == HTTPStatus.OK assert resp.json() == {"resource": "ok", "user_email": user.email} # With the refresh token, we can retrieve a new access token. resp = Client().post( headless_reverse("headless:tokens:refresh"), data={"refresh_token": refresh_token}, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK new_access_token = resp.json()["data"]["access_token"] assert new_access_token != access_token # And, of course, that new access token is valid. at_client = Client(HTTP_AUTHORIZATION=f"Bearer {new_access_token}") resp = at_client.get(headless_reverse("headless:account:current_session")) assert resp.status_code == HTTPStatus.OK # Also check validity with DRF & Ninja endpoints for url in [ reverse("headless_rest_framework_resource"), "/headless/ninja/resource", ]: resp = Client(HTTP_AUTHORIZATION=f"Bearer {access_token}").get( f"{url}?userinfo" ) assert resp.status_code == HTTPStatus.OK assert resp.json() == {"resource": "ok", "user_email": user.email} # But, when we logout... resp = at_client.delete(headless_reverse("headless:account:current_session")) assert resp.status_code == HTTPStatus.UNAUTHORIZED # ... the refresh token no longer works. resp = Client().post( headless_reverse("headless:tokens:refresh"), data={"refresh_token": refresh_token}, content_type="application/json", ) assert resp.status_code == HTTPStatus.BAD_REQUEST # And, the access token no longer functions... at_client = Client(HTTP_AUTHORIZATION=f"Bearer {new_access_token}") resp = at_client.get(headless_reverse("headless:account:current_session")) assert resp.status_code == ( HTTPStatus.UNAUTHORIZED if stateful_validation_enabled else HTTPStatus.GONE ) # Also check validity with DRF & Ninja endpoints for url in [ reverse("headless_rest_framework_resource"), "/headless/ninja/resource", ]: resp = Client(HTTP_AUTHORIZATION=f"Bearer {access_token}").get(url) assert resp.status_code == ( HTTPStatus.UNAUTHORIZED if stateful_validation_enabled else HTTPStatus.OK ) @pytest.mark.parametrize("stateful,query_count", [(False, 0), (True, 1)]) @pytest.mark.parametrize( "url", [ reverse_lazy("headless_rest_framework_resource"), "/headless/ninja/resource", ], ) def test_access_token_query_counts( headless_client, headless_reverse, client, settings, obtain_tokens, django_assert_num_queries, stateful, query_count, url, ): if headless_client == "browser": return settings.HEADLESS_TOKEN_STRATEGY = ( "allauth.headless.tokens.strategies.jwt.JWTTokenStrategy" ) settings.HEADLESS_JWT_STATEFUL_VALIDATION_ENABLED = stateful access_token, _ = obtain_tokens(client) with django_assert_num_queries(query_count): resp = Client(HTTP_AUTHORIZATION=f"Bearer {access_token}").get(url) assert resp.status_code == (HTTPStatus.OK) @pytest.mark.parametrize("settings_scheme", ["Bearer", "Token", "JWT"]) @pytest.mark.parametrize("request_scheme", ["Bearer", "Token", "JWT"]) def test_custom_authorization_header_scheme( headless_client, headless_reverse, client, settings, obtain_tokens, settings_scheme, request_scheme, ): """Test that JWT_AUTHORIZATION_HEADER_SCHEME setting is respected.""" if headless_client == "browser": return # Set authorization header scheme in settings settings.HEADLESS_JWT_AUTHORIZATION_HEADER_SCHEME = settings_scheme settings.HEADLESS_TOKEN_STRATEGY = ( "allauth.headless.tokens.strategies.jwt.JWTTokenStrategy" ) access_token, _ = obtain_tokens(client) # Make request with the specified scheme at_client = Client(HTTP_AUTHORIZATION=f"{request_scheme} {access_token}") resp = at_client.get(headless_reverse("headless:account:current_session")) # Should succeed only when request scheme matches settings scheme if settings_scheme == request_scheme: assert resp.status_code == HTTPStatus.OK else: assert resp.status_code == HTTPStatus.UNAUTHORIZED def test_hs256_algorithm( headless_client, headless_reverse, client, settings, user, obtain_tokens, ): if headless_client == "browser": return settings.HEADLESS_JWT_ALGORITHM = "HS256" settings.HEADLESS_JWT_PRIVATE_KEY = "super-secret" settings.HEADLESS_TOKEN_STRATEGY = ( "allauth.headless.tokens.strategies.jwt.JWTTokenStrategy" ) access_token, _ = obtain_tokens(client) header = jwt.get_unverified_header(access_token) assert header["alg"] == "HS256" assert "kid" not in header payload = jwt.decode( access_token, key="super-secret", algorithms=["HS256"], options={"verify_signature": True, "verify_iss": False, "verify_aud": False}, ) assert payload["sub"] == str(user.pk) at_client = Client(HTTP_AUTHORIZATION=f"Bearer {access_token}") resp = at_client.get(headless_reverse("headless:account:current_session")) assert resp.status_code == HTTPStatus.OK def test_hs256_fallback_to_secret_key( headless_client, headless_reverse, client, settings, user, obtain_tokens, ): if headless_client == "browser": return settings.HEADLESS_JWT_ALGORITHM = "HS256" settings.HEADLESS_JWT_PRIVATE_KEY = "" # Empty settings.SECRET_KEY = "django-secret-fallback" settings.HEADLESS_TOKEN_STRATEGY = ( "allauth.headless.tokens.strategies.jwt.JWTTokenStrategy" ) access_token, _ = obtain_tokens(client) payload = jwt.decode( access_token, key="django-secret-fallback", algorithms=["HS256"], options={"verify_signature": True, "verify_iss": False, "verify_aud": False}, ) assert payload["sub"] == str(user.pk) ================================================ FILE: tests/apps/headless/tokens/test_tokens.py ================================================ from http import HTTPStatus from allauth.headless.tokens.strategies.sessions import SessionTokenStrategy class DummyAccessTokenStrategy(SessionTokenStrategy): def create_access_token(self, request): return f"at-user-{request.user.pk}" def test_access_token( client, user, user_password, settings, headless_reverse, headless_client, ): settings.HEADLESS_TOKEN_STRATEGY = ( "tests.apps.headless.tokens.test_tokens.DummyAccessTokenStrategy" ) resp = client.post( headless_reverse("headless:account:login"), data={ "username": user.username, "password": user_password, }, content_type="application/json", ) data = resp.json() assert data["status"] == HTTPStatus.OK if headless_client == "app": assert data["meta"]["access_token"] == f"at-user-{user.pk}" else: assert "access_token" not in data["meta"] ================================================ FILE: tests/apps/headless/usersessions/__init__.py ================================================ ================================================ FILE: tests/apps/headless/usersessions/test_views.py ================================================ from http import HTTPStatus from allauth.usersessions.models import UserSession def test_flow(client, user, user_password, headless_reverse, settings): settings.ACCOUNT_LOGIN_METHODS = {"email"} resp = client.post( headless_reverse("headless:account:login"), data={ "email": user.email, "password": user_password, }, content_type="application/json", ) assert resp.status_code == HTTPStatus.OK resp = client.get(headless_reverse("headless:usersessions:sessions")) assert resp.status_code == HTTPStatus.OK data = resp.json() assert len(data["data"]) == 1 session_pk = data["data"][0]["id"] assert UserSession.objects.filter(pk=session_pk).exists() resp = client.delete( headless_reverse("headless:usersessions:sessions"), data={"sessions": [session_pk]}, content_type="application/json", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED assert not UserSession.objects.filter(pk=session_pk).exists() ================================================ FILE: tests/apps/idp/__init__.py ================================================ ================================================ FILE: tests/apps/idp/oidc/__init__.py ================================================ ================================================ FILE: tests/apps/idp/oidc/conftest.py ================================================ import time import uuid from datetime import timedelta from types import SimpleNamespace from django.utils import timezone import pytest from allauth.core.context import request_context from allauth.idp.oidc.adapter import get_adapter from allauth.idp.oidc.internal.oauthlib.request_validator import ( OAuthLibRequestValidator, ) from allauth.idp.oidc.models import Client, Token @pytest.fixture def oidc_client_secret(): return uuid.uuid4().hex @pytest.fixture def oidc_client(db, oidc_client_secret): client = Client.objects.create() client.set_secret(oidc_client_secret) client.set_redirect_uris(["https://client/callback"]) client.set_scopes(["profile", "openid", "email"]) client.set_grant_types([g.value for g in Client.GrantType]) client.set_response_types(["code", "token"]) client.save() return client @pytest.fixture def device_client(db): client = Client.objects.create() client.type = Client.Type.PUBLIC client.set_redirect_uris(["https://client/callback"]) client.set_scopes(["profile", "openid", "email"]) client.set_grant_types([g.value for g in Client.GrantType]) client.set_response_types(["code", "token"]) client.save() return client @pytest.fixture def id_token_generator(rf): def f(client, user): with request_context(rf.get("/")): request = SimpleNamespace(client=client, user=user, scopes=["openid"]) return OAuthLibRequestValidator().finalize_id_token( { "aud": client.id, "iat": int(time.time()), }, {}, None, request, ) return f @pytest.fixture def access_token_generator(): def f(client, user, scopes=["openid"]): token = uuid.uuid4().hex token_hash = get_adapter().hash_token(token) instance = Token( type=Token.Type.ACCESS_TOKEN, user=user, client=client, hash=token_hash, ) instance.set_scopes(scopes) instance.save() return token, instance return f @pytest.fixture def refresh_token_factory(): def f(*, user, client, scopes=None): adapter = get_adapter() value = uuid.uuid4().hex rt = Token.objects.create( client=client, user=user, type=Token.Type.REFRESH_TOKEN, hash=adapter.hash_token(value), expires_at=timezone.now() + timedelta(seconds=60), ) if scopes is None: scopes = ["openid", "profile"] rt.set_scopes(scopes) rt.save() return value, rt return f ================================================ FILE: tests/apps/idp/oidc/contrib/__init__.py ================================================ ================================================ FILE: tests/apps/idp/oidc/contrib/ninja/__init__.py ================================================ ================================================ FILE: tests/apps/idp/oidc/contrib/ninja/test_views.py ================================================ from http import HTTPStatus def test_resource(db, client, access_token_generator, user, oidc_client): token, _ = access_token_generator( client=oidc_client, user=user, scopes=["view-resource"] ) resp = client.get("/idp/ninja/resource", HTTP_AUTHORIZATION=f"bearer {token}") assert resp.status_code == HTTPStatus.OK assert resp.json()["user_email"] == user.email def test_resource_using_id_token(db, client, id_token_generator, user, oidc_client): token = id_token_generator(client=oidc_client, user=user) resp = client.get("/idp/ninja/resource", HTTP_AUTHORIZATION=f"bearer {token}") assert resp.status_code == HTTPStatus.UNAUTHORIZED def test_resource_forbidden(db, client, access_token_generator, user, oidc_client): token, _ = access_token_generator( client=oidc_client, user=user, scopes=["other-resource"] ) resp = client.get("/idp/ninja/resource", HTTP_AUTHORIZATION=f"bearer {token}") assert resp.status_code == HTTPStatus.UNAUTHORIZED def test_resource_user_inactive(db, client, access_token_generator, user, oidc_client): user.is_active = False user.save(update_fields=["is_active"]) token, _ = access_token_generator( client=oidc_client, user=user, scopes=["view-resource"] ) resp = client.get("/idp/ninja/resource", HTTP_AUTHORIZATION=f"bearer {token}") assert resp.status_code == HTTPStatus.UNAUTHORIZED ================================================ FILE: tests/apps/idp/oidc/contrib/rest_framework/__init__.py ================================================ ================================================ FILE: tests/apps/idp/oidc/contrib/rest_framework/test_views.py ================================================ from http import HTTPStatus from django.urls import reverse def test_resource(db, client, access_token_generator, user, oidc_client): token, _ = access_token_generator( client=oidc_client, user=user, scopes=["view-resource"] ) resp = client.get( reverse("idp_rest_framework_resource"), HTTP_AUTHORIZATION=f"bearer {token}" ) assert resp.status_code == HTTPStatus.OK assert resp.json()["user_email"] == user.email def test_resource_without_user(db, client, access_token_generator, oidc_client): token, _ = access_token_generator( client=oidc_client, user=None, scopes=["view-resource"] ) resp = client.get( reverse("idp_rest_framework_resource"), HTTP_AUTHORIZATION=f"bearer {token}" ) assert resp.status_code == HTTPStatus.OK assert resp.json()["user_email"] is None def test_resource_forbidden(db, client, access_token_generator, user, oidc_client): token, _ = access_token_generator( client=oidc_client, user=user, scopes=["other-resource"] ) resp = client.get( reverse("idp_rest_framework_resource"), HTTP_AUTHORIZATION=f"bearer {token}" ) assert resp.status_code == HTTPStatus.FORBIDDEN ================================================ FILE: tests/apps/idp/oidc/internal/__init__.py ================================================ ================================================ FILE: tests/apps/idp/oidc/internal/oauthlib/__init__.py ================================================ ================================================ FILE: tests/apps/idp/oidc/internal/oauthlib/test_request_validator.py ================================================ from types import SimpleNamespace import pytest from allauth.idp.oidc.internal.oauthlib.request_validator import ( OAuthLibRequestValidator, ) @pytest.mark.parametrize( "origin,allowed_origins,is_allowed", [ ("http://origin", ["https://origin"], False), ("https://origin", ["https://origin"], True), ("https://origin", [], False), ("https://origin", ["https://notthis", "https://origin"], True), ], ) def test_is_origin_allowed(origin, allowed_origins, is_allowed, oidc_client): oidc_client.set_cors_origins(allowed_origins) oidc_client.save() request = SimpleNamespace() assert ( OAuthLibRequestValidator().is_origin_allowed(oidc_client.id, origin, request) == is_allowed ) ================================================ FILE: tests/apps/idp/oidc/internal/oauthlib/test_utils.py ================================================ import pytest from allauth.idp.oidc.internal.oauthlib.utils import get_uri @pytest.mark.parametrize( "input,output", [ ("/foo?q=param", "/foo?q=param"), ( "/foo?q=a|b&c=c^d&p=`", "/foo?q=a%7Cb&c=c%5Ed&p=%60", ), ], ) def test_get_uri(rf, input, output): request = rf.get(input) assert get_uri(request) == output ================================================ FILE: tests/apps/idp/oidc/test_authorization.py ================================================ from http import HTTPStatus from unittest.mock import ANY from urllib.parse import parse_qs, urlparse from django.test import Client from django.urls import reverse from django.utils.http import urlencode import jwt import pytest from pytest_django.asserts import assertTemplateUsed from allauth.account.models import EmailAddress from allauth.idp.oidc.models import Token from allauth.socialaccount.providers.oauth2.utils import generate_code_challenge def test_cancel_authorization(auth_client, oidc_client): redirect_uri = oidc_client.get_redirect_uris()[0] resp = auth_client.get( reverse("idp:oidc:authorization") + "?" + urlencode( { "client_id": oidc_client.id, "response_type": "code", "redirect_uri": redirect_uri, } ) ) assert resp.status_code == HTTPStatus.OK assertTemplateUsed(resp, "idp/oidc/authorization_form.html") resp = auth_client.post( reverse("idp:oidc:authorization"), { "request": resp.context["form"]["request"].value(), }, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == f"{redirect_uri}?error=access_denied" @pytest.mark.parametrize( "access_token_format,scopes,has_secondary_email,choose_secondary_email", [ ("opaque", ("openid", "profile", "email"), False, False), ("opaque", ("openid", "profile", "email"), True, False), ("opaque", ("openid", "profile", "email"), True, True), ("opaque", ("openid", "profile"), False, False), ("opaque", ("openid",), False, False), ("jwt", ("openid",), False, False), ], ) def test_authorization_code_flow( auth_client, user, oidc_client, oidc_client_secret, enable_cache, scopes, has_secondary_email, choose_secondary_email, email_factory, settings, access_token_format, ): settings.IDP_OIDC_ACCESS_TOKEN_FORMAT = access_token_format secondary_email = email_factory() EmailAddress.objects.create( user=user, email=secondary_email, primary=False, verified=True, ) redirect_uri = oidc_client.get_redirect_uris()[0] resp = auth_client.get( reverse("idp:oidc:authorization") + "?" + urlencode( { "client_id": oidc_client.id, "redirect_uri": redirect_uri, "response_type": "code", "scope": " ".join(scopes), "nonce": "some-nonce", "state": "some-state", } ) ) assert resp.status_code == HTTPStatus.OK assertTemplateUsed(resp, "idp/oidc/authorization_form.html") post_data = { "scopes": scopes, "action": "grant", "request": resp.context["form"]["request"].value(), } expected_email = secondary_email if choose_secondary_email else user.email if has_secondary_email and "email" in scopes: post_data["email"] = expected_email resp = auth_client.post( reverse("idp:oidc:authorization"), post_data, ) assert resp.status_code == HTTPStatus.FOUND redirected_uri = resp["location"] assert redirected_uri.startswith(redirected_uri) parts = urlparse(redirected_uri) params = parse_qs(parts.query) code = params["code"][0] assert params["state"][0] == "some-state" resp = auth_client.post( reverse("idp:oidc:token"), { "code": code, "grant_type": "authorization_code", "client_id": oidc_client.id, "client_secret": oidc_client_secret, "redirect_uri": redirect_uri, }, ) assert resp.status_code == HTTPStatus.OK data = resp.json() assert set(data.keys()) == { "access_token", "expires_in", "token_type", "scope", "refresh_token", "id_token", } access_token = Token.objects.lookup(Token.Type.ACCESS_TOKEN, data["access_token"]) assert bool(access_token.get_scope_email()) == bool( "email" in scopes and has_secondary_email and choose_secondary_email ) # Access token if access_token_format == "jwt": decoded = jwt.decode(data["access_token"], options={"verify_signature": False}) assert decoded == { "client_id": oidc_client.id, "exp": ANY, "iat": ANY, "iss": "http://testserver", "jti": ANY, "scope": " ".join(scopes), "sub": str(user.pk), "token_use": "access", } # ID token id_token = data["id_token"] decoded = jwt.decode(id_token, options={"verify_signature": False}) assert decoded["sub"] == str(user.pk) assert decoded["nonce"] == "some-nonce" if "email" in scopes: assert decoded["email"] == expected_email else: assert "email" not in decoded if "profile" in scopes: assert decoded["preferred_username"] == user.username else: assert "preferred_username" not in decoded def test_authorization_code_flow_skip_consent( auth_client, user, oidc_client, oidc_client_secret, enable_cache ): oidc_client.skip_consent = True oidc_client.save() redirect_uri = oidc_client.get_redirect_uris()[0] resp = auth_client.get( reverse("idp:oidc:authorization") + "?" + urlencode( { "client_id": oidc_client.id, "response_type": "code", "scope": "openid profile email", "nonce": "some-nonce", "state": "some-state", "redirect_uri": redirect_uri, } ) ) assert resp.status_code == HTTPStatus.FOUND redirected_uri = resp["location"] assert redirected_uri.startswith(redirect_uri) parts = urlparse(redirected_uri) params = parse_qs(parts.query) code = params["code"][0] resp = auth_client.post( reverse("idp:oidc:token"), { "code": code, "grant_type": "authorization_code", "client_id": oidc_client.id, "client_secret": oidc_client_secret, "redirect_uri": redirect_uri, }, ) assert resp.status_code == HTTPStatus.OK data = resp.json() assert set(data.keys()) == { "access_token", "expires_in", "token_type", "scope", "refresh_token", "id_token", } def test_authorization_id_token_hint_match( user, id_token_generator, oidc_client, auth_client, user_factory ): redirect_uri = oidc_client.get_redirect_uris()[0] # Pass along ID token as hint resp = auth_client.get( reverse("idp:oidc:authorization") + "?" + urlencode( { "client_id": oidc_client.id, "id_token_hint": id_token_generator(oidc_client, user), "response_type": "code", "scope": "openid", "nonce": "some-nonce", "state": "some-state", "redirect_uri": redirect_uri, } ) ) assert resp.status_code == HTTPStatus.OK def test_authorization_id_token_hint_mismatch( user, id_token_generator, oidc_client, auth_client, user_factory ): redirect_uri = oidc_client.get_redirect_uris()[0] # Pass along ID token as hint resp = auth_client.get( reverse("idp:oidc:authorization") + "?" + urlencode( { "client_id": oidc_client.id, "id_token_hint": id_token_generator(oidc_client, user_factory()), "response_type": "code", "redirect_uri": redirect_uri, "scope": "openid", "nonce": "some-nonce", "state": "some-state", } ) ) assert resp.status_code == HTTPStatus.FOUND parts = urlparse(resp["location"]) params = parse_qs(parts.query) assert params["error"] == ["login_required"] assert params["error_description"] == [ "Session user does not match client supplied user." ] def test_authorization_post_redirects_to_get(auth_client): payload = { "client_id": "c123", "response_type": "code", "scope": "openid", "nonce": "some-nonce", "state": "some-state", } resp = auth_client.post(reverse("idp:oidc:authorization"), data=payload) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("idp:oidc:authorization") + "?" + urlencode( payload ) def test_authorization_post_redirects_anon_to_get(db, client): payload = { "client_id": "c123", "response_type": "code", "scope": "openid", "nonce": "some-nonce", "state": "some-state", } resp = client.post(reverse("idp:oidc:authorization"), data=payload, follow=True) assert resp.status_code == HTTPStatus.OK url = f"{reverse('idp:oidc:authorization')}?{urlencode(payload)}" assert resp.redirect_chain == [ (url, HTTPStatus.FOUND), ( reverse("account_login") + "?" + urlencode({"next": url}).replace("%2F", "/"), HTTPStatus.FOUND, ), ] def test_authorization_post_is_csrf_protected(user): client = Client(enforce_csrf_checks=True) client.force_login(user) payload = { "request": "dummy", "scopes": "openid", } resp = client.post(reverse("idp:oidc:authorization"), data=payload) assert resp.status_code == HTTPStatus.FORBIDDEN assert b"CSRF Failed" in resp.content @pytest.mark.parametrize("valid_code_verifier", [False, True]) def test_authorization_code_flow_with_pkce( auth_client, user, oidc_client, enable_cache, valid_code_verifier, ): oidc_client.type = oidc_client.Type.PUBLIC oidc_client.save() redirect_uri = oidc_client.get_redirect_uris()[0] scopes = ["openid", "profile", "email"] pkce = generate_code_challenge() resp = auth_client.get( reverse("idp:oidc:authorization") + "?" + urlencode( { "client_id": oidc_client.id, "redirect_uri": redirect_uri, "response_type": "code", "scope": " ".join(scopes), "nonce": "some-nonce", "state": "some-state", "code_challenge": pkce["code_challenge"], "code_challenge_method": pkce["code_challenge_method"], } ) ) assert resp.status_code == HTTPStatus.OK assertTemplateUsed(resp, "idp/oidc/authorization_form.html") resp = auth_client.post( reverse("idp:oidc:authorization"), { "scopes": scopes, "action": "grant", "request": resp.context["form"]["request"].value(), }, ) assert resp.status_code == HTTPStatus.FOUND redirected_uri = resp["location"] assert redirected_uri.startswith(redirected_uri) parts = urlparse(redirected_uri) params = parse_qs(parts.query) code = params["code"][0] assert params["state"][0] == "some-state" resp = auth_client.post( reverse("idp:oidc:token"), { "grant_type": "authorization_code", "code": code, "client_id": oidc_client.id, "redirect_uri": redirect_uri, "state": "some-state", "code_verifier": pkce["code_verifier"] if valid_code_verifier else "WRONG", }, ) if not valid_code_verifier: assert resp.status_code == HTTPStatus.BAD_REQUEST return assert resp.status_code == HTTPStatus.OK data = resp.json() assert set(data.keys()) == { "access_token", "expires_in", "id_token", "token_type", "scope", "refresh_token", } @pytest.mark.parametrize("client_fixture", ["auth_client", "client"]) @pytest.mark.parametrize( "prompt,next_prompt", [("login", None), ("login consent", "consent")] ) def test_redirect_to_login_with_prompt_login( request, client_fixture, oidc_client, prompt, next_prompt ): client = request.getfixturevalue(client_fixture) redirect_uri = oidc_client.get_redirect_uris()[0] resp = client.get( reverse("idp:oidc:authorization") + "?" + urlencode( { "client_id": oidc_client.id, "response_type": "code", "redirect_uri": redirect_uri, "prompt": prompt, } ) ) assert resp.status_code == HTTPStatus.FOUND parts = urlparse(resp["location"]) assert parts.path == reverse( "account_login" if client_fixture == "client" else "account_reauthenticate" ) params = parse_qs(parts.query) params = parse_qs(urlparse(params["next"][0]).query) if next_prompt: assert params["prompt"][0] == next_prompt else: assert "prompt" not in params @pytest.mark.parametrize( "client_fixture,scope,error", [ ("auth_client", "openid", None), ("auth_client", "openid profile", "consent_required"), ("client", "openid", "login_required"), ], ) def test_prompt_none( request, client_fixture, scope, error, oidc_client, user, access_token_generator, ): access_token_generator(oidc_client, user, scopes=["openid"]) client = request.getfixturevalue(client_fixture) redirect_uri = oidc_client.get_redirect_uris()[0] resp = client.get( reverse("idp:oidc:authorization") + "?" + urlencode( { "client_id": oidc_client.id, "response_type": "code", "scope": scope, "redirect_uri": redirect_uri, "prompt": "none", } ) ) assert resp.status_code == HTTPStatus.FOUND if error: assert resp["location"] == f"https://client/callback?error={error}" else: assert resp["location"].startswith("https://client/callback?code=") ================================================ FILE: tests/apps/idp/oidc/test_device.py ================================================ from http import HTTPStatus from unittest.mock import ANY from django.core.cache import cache from django.urls import reverse from django.utils.http import urlencode import pytest from pytest_django.asserts import assertTemplateUsed from allauth.idp.oidc import app_settings from allauth.idp.oidc.internal.oauthlib import device_codes from allauth.idp.oidc.models import Client, Token @pytest.fixture def poll_sleep(): def f(device_code, seconds): data = cache.get(device_codes.cache_device_code_key(device_code)) if data: data["last_poll_at"] -= seconds device_codes.update_device_state(device_code, data) return f def test_device_flow_invalid_client( db, client, ): payload = { "client_id": "unknown", } resp = client.post( reverse("idp:oidc:device_code"), data=urlencode(payload), content_type="application/x-www-form-urlencoded", ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "error": "invalid_request", "error_description": "Invalid client_id parameter value.", } def test_device_flow_invalid_scope(db, client, device_client): payload = { "client_id": device_client.id, "scope": "openid wrong", } resp = client.post( reverse("idp:oidc:device_code"), data=urlencode(payload), content_type="application/x-www-form-urlencoded", ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == {"error": "invalid_scope"} @pytest.mark.parametrize("action", ["deny", "confirm"]) @pytest.mark.parametrize("with_scope", [False, True]) def test_device_flow( client, auth_client, device_client, enable_cache, action, user, with_scope, poll_sleep, ): device_client.set_default_scopes(["profile"]) device_client.save() payload = { "client_id": device_client.id, } if with_scope: payload["scope"] = "openid email" resp = client.post( reverse("idp:oidc:device_code"), data=urlencode(payload), content_type="application/x-www-form-urlencoded", ) assert resp.status_code == HTTPStatus.OK data = resp.json() assert data == { "verification_uri": "http://testserver/identity/o/device", "verification_uri_complete": ANY, "expires_in": app_settings.DEVICE_CODE_EXPIRES_IN, "user_code": ANY, "device_code": ANY, "interval": 5, } assert ( data["verification_uri_complete"] == f"{data['verification_uri']}?code={data['user_code']}" ) resp = client.post( reverse("idp:oidc:token"), { "device_code": data["device_code"], "grant_type": Client.GrantType.DEVICE_CODE, "client_id": device_client.id, }, ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "error": "authorization_pending", } resp = auth_client.get(data["verification_uri"]) assert resp.status_code == HTTPStatus.OK assertTemplateUsed(resp, "idp/oidc/device_authorization_code_form.html") assert resp.context["form"].errors == {} resp = auth_client.get(f"{data['verification_uri']}?code=wrong") assert resp.status_code == HTTPStatus.OK assertTemplateUsed(resp, "idp/oidc/device_authorization_code_form.html") assert resp.context["form"].errors == {"code": ["Incorrect code."]} resp = auth_client.get(data["verification_uri_complete"]) assert resp.status_code == HTTPStatus.OK assertTemplateUsed(resp, "idp/oidc/device_authorization_confirm_form.html") confirmed = action == "confirm" resp = auth_client.post(data["verification_uri_complete"], {"action": action}) assert resp.status_code == HTTPStatus.OK assertTemplateUsed( resp, ( "idp/oidc/device_authorization_confirmed.html" if confirmed else "idp/oidc/device_authorization_denied.html" ), ) poll_sleep(data["device_code"], 10) resp = client.post( reverse("idp:oidc:token"), { "device_code": data["device_code"], "grant_type": Client.GrantType.DEVICE_CODE, "client_id": device_client.id, }, ) if confirmed: assert resp.status_code == HTTPStatus.OK assert resp.json() == { "access_token": ANY, "expires_in": 3600, "refresh_token": ANY, "scope": "openid email" if with_scope else "profile", "token_type": "Bearer", } token = Token.objects.lookup( Token.Type.ACCESS_TOKEN, resp.json()["access_token"] ) assert token.user_id == user.pk # Single-use device codes. poll_sleep(data["device_code"], 10) resp = client.post( reverse("idp:oidc:token"), { "device_code": data["device_code"], "grant_type": Client.GrantType.DEVICE_CODE, "client_id": device_client.id, }, ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "error": "invalid_grant", } else: assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "error": "access_denied", } def test_slow_down_flow(client, device_client, enable_cache, poll_sleep): payload = { "client_id": device_client.id, } resp = client.post( reverse("idp:oidc:device_code"), data=urlencode(payload), content_type="application/x-www-form-urlencoded", ) assert resp.status_code == HTTPStatus.OK data = resp.json() for sleep, error in [ (0, "authorization_pending"), (3, "slow_down"), (3, "authorization_pending"), ]: if sleep: poll_sleep(data["device_code"], sleep) resp = client.post( reverse("idp:oidc:token"), { "device_code": data["device_code"], "grant_type": Client.GrantType.DEVICE_CODE, "client_id": device_client.id, }, ) assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == {"error": error} ================================================ FILE: tests/apps/idp/oidc/test_rp_initiated_logout.py ================================================ from http import HTTPStatus from django.urls import reverse from django.utils.http import urlencode import pytest from allauth.idp.oidc.models import Token @pytest.mark.parametrize("method", ["GET", "POST"]) @pytest.mark.parametrize( "post_logout_redirect_uri,state,expected_location", [ (None, None, "/"), ("https://rp.client/logged-out", None, "https://rp.client/logged-out"), ( "https://rp.client/logged-out", "mystate", "https://rp.client/logged-out?state=mystate", ), ("http://no-http.org/please", None, "/"), ], ) def test_logout_while_anonymous( method, client, post_logout_redirect_uri, state, expected_location ): params = {} if post_logout_redirect_uri: params["post_logout_redirect_uri"] = post_logout_redirect_uri if state: params["state"] = state if method == "GET": query = None if params: query = urlencode(params) resp = client.get(reverse("idp:oidc:logout") + (f"?{query}" if query else "")) else: resp = client.post(reverse("idp:oidc:logout"), data=params) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == expected_location @pytest.mark.parametrize("method", ["GET", "POST"]) def test_logout_must_ask(auth_client, settings, method): settings.IDP_OIDC_RP_INITIATED_LOGOUT_ASKS_FOR_OP_LOGOUT = False params = {} if method == "GET": query = None if params: query = urlencode(params) resp = auth_client.get( reverse("idp:oidc:logout") + (f"?{query}" if query else "") ) else: resp = auth_client.post(reverse("idp:oidc:logout"), data=params) assert resp.status_code == HTTPStatus.OK @pytest.mark.parametrize( "csrfmiddlewaretoken, status_code", [(None, HTTPStatus.OK), ("", HTTPStatus.FORBIDDEN), ("hack", HTTPStatus.FORBIDDEN)], ) def test_rp_cannot_bypass(auth_client, csrfmiddlewaretoken, status_code): auth_client.enforce_csrf_checks = True auth_client.handler.enforce_csrf_checks = True params = { # Try a POST that answers the question right away... "action": "logout", } if csrfmiddlewaretoken is not None: params["csrfmiddlewaretoken"] = "hack" resp = auth_client.post(reverse("idp:oidc:logout"), data=params) assert resp.status_code == status_code @pytest.mark.parametrize("method", ["GET", "POST"]) def test_logout_without_asking( auth_client, user, settings, method, oidc_client, id_token_generator, access_token_generator, refresh_token_factory, ): id_token_hint = id_token_generator(oidc_client, user) access_token, access_token_instance = access_token_generator(oidc_client, user) refresh_token, refresh_token_instance = refresh_token_factory( user=user, client=oidc_client ) settings.IDP_OIDC_RP_INITIATED_LOGOUT_ASKS_FOR_OP_LOGOUT = False params = { "id_token_hint": id_token_hint, "post_logout_redirect_uri": "https://rp.client/logged-out", } if method == "GET": query = None if params: query = urlencode(params) resp = auth_client.get( reverse("idp:oidc:logout") + (f"?{query}" if query else "") ) else: resp = auth_client.post(reverse("idp:oidc:logout"), data=params) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == "https://rp.client/logged-out" assert not Token.objects.filter(pk=access_token_instance.pk).exists() assert not Token.objects.filter(pk=refresh_token_instance.pk).exists() resp = auth_client.get(reverse("account_email")) assert resp.status_code == HTTPStatus.FOUND assert resp["location"].startswith(reverse("account_login")) ================================================ FILE: tests/apps/idp/oidc/test_tokens.py ================================================ from http import HTTPStatus from unittest.mock import ANY from django.urls import reverse import pytest from allauth.idp.oidc.models import Token def test_userinfo_bad_token(client, oidc_client, user): # Pass along ID token as hint resp = client.get(reverse("idp:oidc:userinfo")) assert resp.status_code == HTTPStatus.UNAUTHORIZED assert resp.json() == { "error": "invalid_token", "error_description": "The access token provided is expired, revoked, malformed, or invalid for other reasons.", } def test_revoke_access_token( client, oidc_client, oidc_client_secret, user, access_token_generator ): token, instance = access_token_generator(oidc_client, user) _, instance_to_keep = access_token_generator(oidc_client, user) resp = client.post( reverse("idp:oidc:revoke"), data={ "client_id": oidc_client.id, "client_secret": oidc_client_secret, "token": token, }, ) assert resp.status_code == HTTPStatus.OK assert not Token.objects.filter(pk=instance.pk).exists() assert Token.objects.filter(pk=instance_to_keep.pk).exists() @pytest.mark.parametrize("rotate_refresh_token", [False, True]) @pytest.mark.parametrize("scopes", [("openid",), ("openid", "email")]) def test_refresh_token( db, client, oidc_client, oidc_client_secret, user, refresh_token_factory, settings, rotate_refresh_token, scopes, ): settings.IDP_OIDC_ROTATE_REFRESH_TOKEN = rotate_refresh_token rt, rt_instance = refresh_token_factory( user=user, client=oidc_client, scopes=scopes ) rt_instance.set_scope_email("a@b.org") rt_instance.save() resp = client.post( reverse("idp:oidc:token"), { "refresh_token": rt, "grant_type": "refresh_token", "client_id": oidc_client.id, "client_secret": oidc_client_secret, }, ) assert resp.status_code == HTTPStatus.OK data = resp.json() new_rt_instance = Token.objects.lookup( Token.Type.REFRESH_TOKEN, data["refresh_token"] ) if rotate_refresh_token: assert data["refresh_token"] != rt assert new_rt_instance.pk != rt_instance.pk else: assert data["refresh_token"] == rt assert Token.objects.filter(type=Token.Type.REFRESH_TOKEN).count() == 1 token = Token.objects.lookup(Token.Type.ACCESS_TOKEN, data["access_token"]) assert token.get_scope_email() == ("a@b.org" if "email" in scopes else None) return assert data == { "access_token": ANY, "expires_in": 3600, "refresh_token": ANY, "scope": "openid profile", "token_type": "Bearer", } def test_revoke_refresh_token( db, client, oidc_client, oidc_client_secret, user, refresh_token_factory ): token_value, token_instance = refresh_token_factory(user=user, client=oidc_client) resp = client.post( reverse("idp:oidc:revoke"), { "token": token_value, "client_id": oidc_client.id, "client_secret": oidc_client_secret, }, ) assert resp.status_code == HTTPStatus.OK assert resp.content == b"" assert not Token.objects.filter(pk=token_instance.pk).exists() ================================================ FILE: tests/apps/idp/oidc/test_views.py ================================================ from http import HTTPStatus from unittest.mock import ANY from urllib.parse import parse_qs, urlparse from django.urls import reverse from django.utils.http import urlencode import jwt import pytest from pytest_django.asserts import assertTemplateUsed from allauth.account.models import EmailAddress from allauth.idp.oidc.adapter import get_adapter from allauth.idp.oidc.models import Token @pytest.mark.parametrize( "scopes,has_secondary_email,choose_secondary_email", [ (("openid",), False, False), (("openid", "email"), False, False), (("openid", "email"), True, True), ], ) def test_userinfo( client, oidc_client, user, access_token_generator, scopes, has_secondary_email, choose_secondary_email, email_factory, ): # Pass along ID token as hint token, token_instance = access_token_generator(oidc_client, user, scopes=scopes) if has_secondary_email: email = email_factory() EmailAddress.objects.create( user=user, email=email, verified=True, primary=False ) token_instance.set_scope_email(email) token_instance.save() expected_email = email if choose_secondary_email else user.email else: expected_email = user.email resp = client.get( reverse("idp:oidc:userinfo"), HTTP_AUTHORIZATION=f"Bearer {token}", ) assert resp.status_code == HTTPStatus.OK data = resp.json() assert data["sub"] == get_adapter().get_user_sub(oidc_client, user) if "email" in scopes: assert data["email"] == expected_email assert data["email_verified"] is True else: assert "email" not in data @pytest.mark.parametrize("access_token_format", ["jwt", "opaque"]) def test_client_credentials( client, oidc_client, oidc_client_secret, access_token_format, settings ): settings.IDP_OIDC_ACCESS_TOKEN_FORMAT = access_token_format resp = client.post( reverse("idp:oidc:token"), data={ "client_id": oidc_client.id, "client_secret": oidc_client_secret, "scope": "profile email", "grant_type": "client_credentials", }, ) assert resp.status_code == HTTPStatus.OK data = resp.json() assert data == { "access_token": ANY, "expires_in": 3600, "scope": "profile email", "token_type": "Bearer", } token = Token.objects.lookup(Token.Type.ACCESS_TOKEN, data["access_token"]) assert token.client == oidc_client assert token.get_scopes() == ["profile", "email"] if access_token_format == "jwt": decoded = jwt.decode(data["access_token"], options={"verify_signature": False}) assert decoded == { "client_id": oidc_client.id, "exp": ANY, "iat": ANY, "iss": "http://testserver", "jti": ANY, "scope": "profile email", "token_use": "access", } def test_password_grant_is_blocked( client, oidc_client, oidc_client_secret, user, user_password ): resp = client.post( reverse("idp:oidc:token"), data={ "client_id": oidc_client.id, "client_secret": oidc_client_secret, # These are valid credentials. "username": user.username, "password": user_password, "scope": "profile email", "grant_type": "password", }, ) # We don't crash, but also don't grant. assert resp.status_code == HTTPStatus.BAD_REQUEST assert resp.json() == { "error": "invalid_grant", "error_description": "Invalid credentials given.", } def test_implicit_grant_flow(auth_client, user, oidc_client, enable_cache): redirect_uri = oidc_client.get_redirect_uris()[0] scopes = ["openid", "profile"] resp = auth_client.get( reverse("idp:oidc:authorization") + "?" + urlencode( { "client_id": oidc_client.id, "response_type": "token", "scope": " ".join(scopes), "nonce": "some-nonce", "state": "some-state", "redirect_uri": redirect_uri, } ) ) assert resp.status_code == HTTPStatus.OK assertTemplateUsed(resp, "idp/oidc/authorization_form.html") resp = auth_client.post( reverse("idp:oidc:authorization"), { "scopes": scopes, "action": "grant", "request": resp.context["form"]["request"].value(), }, ) # "https://client/callback#access_token=baI5uc9m5JWc6afKqaZ9eymeOrq1hz&expires_in=3600&token_type=Bearer&scope=openid+profile&state=some-state" assert resp.status_code == HTTPStatus.FOUND parts = urlparse(resp["location"]) data = parse_qs(parts.fragment) assert data == { "access_token": ANY, "expires_in": ["3600"], "scope": ["openid profile"], "token_type": ["Bearer"], "state": ["some-state"], } def test_userinfo_access_token_as_query( client, oidc_client, user, access_token_generator ): # Pass along ID token as hint token, _ = access_token_generator(oidc_client, user, scopes=["openid"]) resp = client.get( f"{reverse('idp:oidc:userinfo')}?{urlencode({'access_token': token})}", ) assert resp.status_code == HTTPStatus.UNAUTHORIZED def test_jwks_view(client): resp = client.get(reverse("idp:oidc:jwks")) assert resp.status_code == HTTPStatus.OK assert resp.json() == { "keys": [{"e": ANY, "key_ops": ["verify"], "kid": ANY, "kty": "RSA", "n": ANY}] } @pytest.mark.parametrize("custom_userinfo_endpoint", [False, True]) def test_configuration_view( client, oidc_client, custom_userinfo_endpoint, settings_impacting_urls ): with settings_impacting_urls( IDP_OIDC_USERINFO_ENDPOINT=( "https://remote/userinfo" if custom_userinfo_endpoint else None ) ): resp = client.get(reverse("idp:oidc:configuration")) assert resp.status_code == HTTPStatus.OK assert resp.json() == { "authorization_endpoint": "http://testserver/identity/o/authorize", "device_authorization_endpoint": "http://testserver/identity/o/api/device/code", "end_session_endpoint": "http://testserver/identity/o/logout", "id_token_signing_alg_values_supported": ["RS256"], "issuer": "http://testserver", "jwks_uri": "http://testserver/.well-known/jwks.json", "response_types_supported": ["code", "token"], "revocation_endpoint": "http://testserver/identity/o/api/revoke", "subject_types_supported": ["public"], "token_endpoint": "http://testserver/identity/o/api/token", "userinfo_endpoint": ( "https://remote/userinfo" if custom_userinfo_endpoint else "http://testserver/identity/o/api/userinfo" ), } def test_post_userinfo( client, oidc_client, user, access_token_generator, ): # Pass along ID token as hint token, token_instance = access_token_generator(oidc_client, user) resp = client.post( reverse("idp:oidc:userinfo"), HTTP_AUTHORIZATION=f"Bearer {token}", ) assert resp.status_code == HTTPStatus.OK data = resp.json() assert data["sub"] == get_adapter().get_user_sub(oidc_client, user) ================================================ FILE: tests/apps/idp/oidc/test_wildcards.py ================================================ from django.core.exceptions import ValidationError import pytest from allauth.idp.oidc.internal.clientkit import ( _wildcard_to_regex, is_origin_allowed, is_redirect_uri_allowed, ) from allauth.idp.oidc.models import Client URI_TEST_CASES = [ (False, "https://example.com", True), (False, "https://*.example.com", False), (True, "https://example.com", True), (True, "https://*.projectname.pages.dev/", True), (True, "https://*--projectname-com.netlify.app", True), (True, "http://*.localhost:3000", True), (True, "https://example.com/callback", True), (True, "https://*.*.example.com", False), (True, "http*://example.com", False), (True, "https://example.com/callback/*", False), ] @pytest.mark.parametrize( ("allow_wildcards", "uri", "should_pass"), URI_TEST_CASES, ) def test_redirect_uri_wildcard_validation(allow_wildcards, uri, should_pass): client = Client( id="test-client", name="Test Client", allow_uri_wildcards=allow_wildcards, redirect_uris=uri, ) if should_pass: client.clean_redirect_uris() else: with pytest.raises(ValidationError): client.clean_redirect_uris() @pytest.mark.parametrize( ("allow_wildcards", "origin", "should_pass"), URI_TEST_CASES, ) def test_cors_origin_wildcard_validation(allow_wildcards, origin, should_pass): client = Client( id="test-client", name="Test Client", allow_uri_wildcards=allow_wildcards, cors_origins=origin, ) if should_pass: client.clean_cors_origins() else: with pytest.raises(ValidationError): client.clean_cors_origins() def test_wildcards_disabled_by_default(): client = Client( id="test-client", name="Test Client", redirect_uris="https://example.com", ) assert client.allow_uri_wildcards is False client.redirect_uris = "https://*.example.com" with pytest.raises(ValidationError): client.clean_redirect_uris() def test_wildcard_matching_enforcement(): client = Client( id="test-client", name="Test Client", allow_uri_wildcards=True, redirect_uris="https://*.example.com/callback", cors_origins="https://*.example.com", ) assert is_redirect_uri_allowed( "https://subdomain.example.com/callback", client.get_redirect_uris(), client.allow_uri_wildcards, ) assert not is_redirect_uri_allowed( "https://evil-example.com/callback", client.get_redirect_uris(), client.allow_uri_wildcards, ) assert is_origin_allowed( "https://subdomain.example.com", client.get_cors_origins(), client.allow_uri_wildcards, ) assert not is_origin_allowed( "https://evil-example.com", client.get_cors_origins(), client.allow_uri_wildcards, ) assert not is_origin_allowed( "https://user:pass@foo.example.com", client.get_cors_origins(), client.allow_uri_wildcards, ) def test_wildcard_to_regex(): pattern = _wildcard_to_regex("*.example.com") assert pattern.pattern == r"^[^.]+\.example\.com$" assert pattern.match("api.example.com") assert not pattern.match("foo.api.example.com") # * should not match dots assert not pattern.match("evil-example.com") # verify dots aren't treated as wildcards assert not pattern.match("api$example$com") ================================================ FILE: tests/apps/mfa/__init__.py ================================================ ================================================ FILE: tests/apps/mfa/base/__init__.py ================================================ ================================================ FILE: tests/apps/mfa/base/test_adapter.py ================================================ from unittest.mock import patch from allauth.mfa.adapter import get_adapter def test_build_totp_url_encodes_spaces_as_percent(user): """Spaces in the issuer name should be percent-encoded (%20), not encoded as '+', so that authenticator apps display the issuer correctly. """ adapter = get_adapter() with patch.object(adapter, "get_totp_issuer", return_value="My Company"): url = adapter.build_totp_url(user, "JBSWY3DPEHPK3PXP") assert "issuer=My%20Company" in url assert "issuer=My+Company" not in url ================================================ FILE: tests/apps/mfa/base/test_trust.py ================================================ import time from http import HTTPStatus from django.conf import settings from django.urls import reverse import pytest from allauth.mfa import app_settings from allauth.mfa.internal.flows import trust def test_encode_decode(rf): request = rf.get("/") value = trust.encode_trust_cookie( [ trust.IssuedTrust(fingerprint="dated", at=2024), trust.IssuedTrust(fingerprint="good", at=time.time()), ] ) request.COOKIES[app_settings.TRUST_COOKIE_NAME] = value trusts = trust.decode_trust_cookie(request) assert len(trusts) == 1 assert trusts[0].fingerprint == "good" def test_decode_invalid_value(rf): request = rf.get("/") request.COOKIES[app_settings.TRUST_COOKIE_NAME] = "bad" trusts = trust.decode_trust_cookie(request) assert len(trusts) == 0 @pytest.mark.parametrize("action", ["", "trust"]) def test_trust_flow( client, user_with_totp, user_password, totp_validation_bypass, settings_impacting_urls, action, ): with settings_impacting_urls(MFA_TRUST_ENABLED=True): # Login resp = client.post( reverse("account_login"), {"login": user_with_totp.username, "password": user_password}, ) assert resp.status_code == HTTPStatus.FOUND # Complete TOTP assert resp["location"] == reverse("mfa_authenticate") with totp_validation_bypass(): resp = client.post( reverse("mfa_authenticate"), {"code": "123"}, ) assert resp.status_code == HTTPStatus.FOUND # Indicate trust assert resp["location"] == reverse("mfa_trust") resp = client.post( reverse("mfa_trust"), {"action": action}, ) assert resp["location"] == settings.LOGIN_REDIRECT_URL # Sign out resp = client.post( reverse("account_logout"), ) assert resp.status_code == HTTPStatus.FOUND # Sign in resp = client.post( reverse("account_login"), {"login": user_with_totp.username, "password": user_password}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == ( settings.LOGIN_REDIRECT_URL if action == "trust" else reverse("mfa_authenticate") ) ================================================ FILE: tests/apps/mfa/base/test_trust_fingerprint.py ================================================ from allauth.mfa.internal.flows import trust from allauth.mfa.models import Authenticator def test_cookie_encoding(): pass def test_fingerprint_is_stable(user_with_totp, user_with_recovery_codes): fp = trust.create_config_fingerprint(user_with_totp) fp2 = trust.create_config_fingerprint(user_with_totp) assert fp == fp2 def test_fingerprint_changes_on_password_change(user_with_totp, password_factory): fp = trust.create_config_fingerprint(user_with_totp) user_with_totp.set_password(password_factory()) fp2 = trust.create_config_fingerprint(user_with_totp) assert fp != fp2 def test_fingerprint_changes_on_recovery_codes_change( user_with_recovery_codes, password_factory ): fp = trust.create_config_fingerprint(user_with_recovery_codes) auth = Authenticator.objects.get( user=user_with_recovery_codes, type=Authenticator.Type.RECOVERY_CODES ) auth.data["seed"] = password_factory() auth.save() fp2 = trust.create_config_fingerprint(user_with_recovery_codes) assert fp != fp2 def test_fingerprint_changes_on_new_authenticator(user_with_totp): fp = trust.create_config_fingerprint(user_with_totp) Authenticator.objects.create( user=user_with_totp, type=Authenticator.Type.RECOVERY_CODES, data={} ) fp2 = trust.create_config_fingerprint(user_with_totp) assert fp != fp2 def test_fingerprint_changes_on_authenticator_deletion( user_with_totp, user_with_recovery_codes ): fp = trust.create_config_fingerprint(user_with_totp) Authenticator.objects.filter( user=user_with_recovery_codes, type=Authenticator.Type.RECOVERY_CODES ).delete() fp2 = trust.create_config_fingerprint(user_with_totp) assert fp != fp2 ================================================ FILE: tests/apps/mfa/base/test_views.py ================================================ from http import HTTPStatus from unittest.mock import ANY from django.urls import reverse import pytest from pytest_django.asserts import assertTemplateUsed from allauth.account.authentication import AUTHENTICATION_METHODS_SESSION_KEY from allauth.mfa.models import Authenticator def test_reauthentication(auth_client, user_with_recovery_codes): resp = auth_client.get(reverse("mfa_view_recovery_codes")) assert resp.status_code == HTTPStatus.FOUND assert resp["location"].startswith(reverse("account_reauthenticate")) resp = auth_client.get(reverse("mfa_reauthenticate")) assertTemplateUsed(resp, "mfa/reauthenticate.html") authenticator = Authenticator.objects.get( user=user_with_recovery_codes, type=Authenticator.Type.RECOVERY_CODES ) unused_code = authenticator.wrap().get_unused_codes()[0] resp = auth_client.post(reverse("mfa_reauthenticate"), data={"code": unused_code}) assert resp.status_code == HTTPStatus.FOUND resp = auth_client.get(reverse("mfa_view_recovery_codes")) assert resp.status_code == HTTPStatus.OK assertTemplateUsed(resp, "mfa/recovery_codes/index.html") methods = auth_client.session[AUTHENTICATION_METHODS_SESSION_KEY] assert methods[-1] == { "method": "mfa", "type": "recovery_codes", "id": authenticator.pk, "at": ANY, "reauthenticated": True, } @pytest.mark.parametrize( "url_name", ( "mfa_activate_totp", "mfa_index", "mfa_deactivate_totp", ), ) def test_login_required_views(client, url_name): resp = client.get(reverse(url_name)) assert resp.status_code == HTTPStatus.FOUND assert resp["location"].startswith(reverse("account_login")) def test_index(auth_client, user_with_totp): resp = auth_client.get(reverse("mfa_index")) assert "authenticators" in resp.context @pytest.mark.parametrize("allowed", [False, True]) def test_add_email_not_allowed( auth_client, user_with_totp, settings, allowed, ): settings.MFA_ALLOW_UNVERIFIED_EMAIL = allowed resp = auth_client.post( reverse("account_email"), {"action_add": "", "email": "change-to@this.org"}, ) if allowed: assert resp.status_code == HTTPStatus.FOUND else: assert resp.status_code == HTTPStatus.OK assert resp.context["form"].errors == { "email": [ "You cannot add an email address to an account protected by two-factor authentication." ] } def test_add_email_allowed_when_verification_by_code( auth_client, user_with_totp, settings, ): settings.ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True resp = auth_client.post( reverse("account_email"), {"action_add": "", "email": "change-to@this.org"}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_email_verification_sent") ================================================ FILE: tests/apps/mfa/recovery_codes/__init__.py ================================================ ================================================ FILE: tests/apps/mfa/recovery_codes/test_auth.py ================================================ from allauth.mfa import app_settings from allauth.mfa.models import Authenticator from allauth.mfa.recovery_codes.internal.auth import RecoveryCodes def test_flow(user): rc = RecoveryCodes.activate(user) codes = rc.generate_codes() assert len(set(codes)) == app_settings.RECOVERY_CODE_COUNT for code in codes: assert len(code) == app_settings.RECOVERY_CODE_DIGITS for i in range(app_settings.RECOVERY_CODE_COUNT): assert not rc._is_code_used(i) idx = 3 assert rc.validate_code(codes[idx]) for i in range(app_settings.RECOVERY_CODE_COUNT): assert rc._is_code_used(i) == (i == idx) assert not rc.validate_code(codes[idx]) unused_codes = rc.get_unused_codes() assert codes[idx] not in unused_codes assert len(unused_codes) == app_settings.RECOVERY_CODE_COUNT - 1 def test_migrated_codes(db, user): auth = Authenticator(user=user, data={"migrated_codes": ["abc", "def"]}) rc = RecoveryCodes(auth) assert rc.generate_codes() == ["abc", "def"] assert rc.get_unused_codes() == ["abc", "def"] assert not rc.validate_code("bad") assert rc.validate_code("abc") auth.refresh_from_db() rc = RecoveryCodes(auth) assert rc.generate_codes() == ["def"] assert rc.get_unused_codes() == ["def"] rc.validate_code("def") assert rc.instance.data["migrated_codes"] == [] ================================================ FILE: tests/apps/mfa/recovery_codes/test_views.py ================================================ from http import HTTPStatus from unittest.mock import ANY from django.conf import settings from django.urls import reverse from allauth.account.authentication import AUTHENTICATION_METHODS_SESSION_KEY from allauth.mfa import app_settings from allauth.mfa.adapter import get_adapter from allauth.mfa.models import Authenticator def test_generate_recovery_codes_require_other_authenticator( auth_client, user, settings, reauthentication_bypass ): with reauthentication_bypass(): resp = auth_client.post(reverse("mfa_generate_recovery_codes")) assert resp.context["form"].errors == { "__all__": [ "You cannot generate recovery codes without having two-factor authentication enabled." ] } assert not Authenticator.objects.filter(user=user).exists() def test_download_recovery_codes(auth_client, user_with_recovery_codes, user_password): resp = auth_client.get(reverse("mfa_download_recovery_codes")) assert resp["location"].startswith(reverse("account_reauthenticate")) resp = auth_client.post(resp["location"], {"password": user_password}) assert resp.status_code == HTTPStatus.FOUND resp = auth_client.get(resp["location"]) assert resp["content-disposition"] == 'attachment; filename="recovery-codes.txt"' def test_view_recovery_codes(auth_client, user_with_recovery_codes, user_password): resp = auth_client.get(reverse("mfa_view_recovery_codes")) assert resp["location"].startswith(reverse("account_reauthenticate")) resp = auth_client.post(resp["location"], {"password": user_password}) assert resp.status_code == HTTPStatus.FOUND resp = auth_client.get(resp["location"]) assert len(resp.context["unused_codes"]) == app_settings.RECOVERY_CODE_COUNT def test_generate_recovery_codes( auth_client, user_with_recovery_codes, user_password, settings, mailoutbox ): settings.ACCOUNT_EMAIL_NOTIFICATIONS = True rc = Authenticator.objects.get( user=user_with_recovery_codes, type=Authenticator.Type.RECOVERY_CODES ).wrap() prev_code = rc.get_unused_codes()[0] resp = auth_client.get(reverse("mfa_generate_recovery_codes")) assert resp["location"].startswith(reverse("account_reauthenticate")) resp = auth_client.post(resp["location"], {"password": user_password}) assert resp.status_code == HTTPStatus.FOUND resp = auth_client.post(resp["location"]) assert resp["location"] == reverse("mfa_view_recovery_codes") rc = Authenticator.objects.get( user=user_with_recovery_codes, type=Authenticator.Type.RECOVERY_CODES ).wrap() assert not rc.validate_code(prev_code) assert len(mailoutbox) == 1 assert "New Recovery Codes Generated" in mailoutbox[0].subject assert "A new set of" in mailoutbox[0].body def test_recovery_codes_login( client, user_with_totp, user_with_recovery_codes, user_password ): resp = client.post( reverse("account_login"), {"login": user_with_totp.username, "password": user_password}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("mfa_authenticate") resp = client.get(reverse("mfa_authenticate")) assert resp.context["request"].user.is_anonymous resp = client.post(reverse("mfa_authenticate"), {"code": "123"}) assert resp.context["form"].errors == { "code": [get_adapter().error_messages["incorrect_code"]] } rc = Authenticator.objects.get( user=user_with_recovery_codes, type=Authenticator.Type.RECOVERY_CODES ) resp = client.post( reverse("mfa_authenticate"), {"code": rc.wrap().get_unused_codes()[0]}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == settings.LOGIN_REDIRECT_URL assert client.session[AUTHENTICATION_METHODS_SESSION_KEY] == [ {"method": "password", "at": ANY, "username": user_with_totp.username}, { "method": "mfa", "at": ANY, "id": ANY, "type": Authenticator.Type.RECOVERY_CODES, }, ] ================================================ FILE: tests/apps/mfa/totp/__init__.py ================================================ ================================================ FILE: tests/apps/mfa/totp/test_unit.py ================================================ from unittest import mock from allauth.mfa import app_settings from allauth.mfa.totp.internal.auth import ( format_hotp_value, generate_totp_secret, hotp_value, validate_totp_code, yield_hotp_counters_from_time, ) @mock.patch("time.time", mock.MagicMock(return_value=1731948631)) def test_totp_counters_from_time(): app_settings.TOTP_TOLERANCE = 0 counters = list(yield_hotp_counters_from_time()) assert len(counters) == 1 @mock.patch("time.time", mock.MagicMock(return_value=1731948631)) def test_totp_counters_from_time_with_tolerance(): app_settings.TOTP_TOLERANCE = 1 counters = list(yield_hotp_counters_from_time()) assert len(counters) == 3 @mock.patch("time.time", mock.MagicMock(return_value=1731948631)) def test_validate_with_tolerance(): app_settings.TOTP_TOLERANCE = 1 test_secret = generate_totp_secret() expected_value = format_hotp_value(hotp_value(test_secret, 57731621)) assert validate_totp_code(test_secret, expected_value) before_value = format_hotp_value(hotp_value(test_secret, 57731620)) assert validate_totp_code(test_secret, before_value) after_value = format_hotp_value(hotp_value(test_secret, 57731622)) assert validate_totp_code(test_secret, after_value) two_before_value = format_hotp_value(hotp_value(test_secret, 57731619)) assert not validate_totp_code(test_secret, two_before_value) two_after_value = format_hotp_value(hotp_value(test_secret, 57731623)) assert not validate_totp_code(test_secret, two_after_value) ================================================ FILE: tests/apps/mfa/totp/test_views.py ================================================ import time from http import HTTPStatus from unittest.mock import ANY, patch from django.conf import settings from django.core.cache import cache from django.test import Client from django.urls import reverse import pytest from pytest_django.asserts import assertTemplateUsed from allauth.account import app_settings from allauth.account.authentication import AUTHENTICATION_METHODS_SESSION_KEY from allauth.mfa.adapter import get_adapter from allauth.mfa.models import Authenticator def test_activate_totp_with_incorrect_code(auth_client, reauthentication_bypass): with reauthentication_bypass(): resp = auth_client.get(reverse("mfa_activate_totp")) resp = auth_client.post( reverse("mfa_activate_totp"), { "code": "123", }, ) assert resp.context["form"].errors == { "code": [get_adapter().error_messages["incorrect_code"]] } @pytest.mark.parametrize("email_verified", [False]) @pytest.mark.parametrize("method", ["get", "post"]) def test_activate_totp_with_unverified_email( auth_client, user, totp_validation_bypass, reauthentication_bypass, method ): with reauthentication_bypass(): if method == "get": resp = auth_client.get(reverse("mfa_activate_totp")) else: resp = auth_client.post(reverse("mfa_activate_totp"), {"code": "123"}) assert resp["location"] == reverse("mfa_index") def test_activate_totp_success( auth_client, totp_validation_bypass, user, reauthentication_bypass, settings, mailoutbox, ): settings.ACCOUNT_EMAIL_NOTIFICATIONS = True with reauthentication_bypass(): resp = auth_client.get(reverse("mfa_activate_totp")) with totp_validation_bypass(): resp = auth_client.post( reverse("mfa_activate_totp"), { "code": "123", }, ) assert resp["location"] == reverse("mfa_view_recovery_codes") assert Authenticator.objects.filter( user=user, type=Authenticator.Type.TOTP ).exists() assert Authenticator.objects.filter( user=user, type=Authenticator.Type.RECOVERY_CODES ).exists() assert len(mailoutbox) == 1 assert "Authenticator App Activated" in mailoutbox[0].subject assert "Authenticator app activated." in mailoutbox[0].body def test_deactivate_totp_success( auth_client, user_with_totp, user_password, settings, mailoutbox ): settings.ACCOUNT_EMAIL_NOTIFICATIONS = True resp = auth_client.get(reverse("mfa_deactivate_totp")) assert resp.status_code == HTTPStatus.FOUND assert resp["location"].startswith(reverse("account_reauthenticate")) resp = auth_client.post(resp["location"], {"password": user_password}) assert resp.status_code == HTTPStatus.FOUND resp = auth_client.post(reverse("mfa_deactivate_totp")) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("mfa_index") assert len(mailoutbox) == 1 assert "Authenticator App Deactivated" in mailoutbox[0].subject assert "Authenticator app deactivated." in mailoutbox[0].body def test_user_without_totp_deactivate_totp(auth_client): resp = auth_client.get(reverse("mfa_deactivate_totp")) assert resp.status_code == HTTPStatus.NOT_FOUND def test_user_with_totp_activate_totp( auth_client, user_with_totp, reauthentication_bypass ): with reauthentication_bypass(): resp = auth_client.get(reverse("mfa_activate_totp")) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("mfa_deactivate_totp") def test_totp_login(client, user_with_totp, user_password, totp_validation_bypass): resp = client.post( reverse("account_login"), {"login": user_with_totp.username, "password": user_password}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("mfa_authenticate") resp = client.get(reverse("mfa_authenticate")) assert resp.context["request"].user.is_anonymous resp = client.post(reverse("mfa_authenticate"), {"code": "123"}) assert resp.context["form"].errors == { "code": [get_adapter().error_messages["incorrect_code"]] } with totp_validation_bypass(): resp = client.post( reverse("mfa_authenticate"), {"code": "123"}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == settings.LOGIN_REDIRECT_URL assert client.session[AUTHENTICATION_METHODS_SESSION_KEY] == [ {"method": "password", "at": ANY, "username": user_with_totp.username}, {"method": "mfa", "at": ANY, "id": ANY, "type": Authenticator.Type.TOTP}, ] def test_totp_login_rate_limit( settings, enable_cache, user_with_totp, user_password, client ): settings.ACCOUNT_LOGIN_ATTEMPTS_LIMIT = 3 resp = client.post( reverse("account_login"), {"login": user_with_totp.username, "password": user_password}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("mfa_authenticate") for i in range(5): is_locked = i >= 3 resp = client.post( reverse("mfa_authenticate"), { "code": "wrong", }, ) assert resp.context["form"].errors == { "code": [ ( "Too many failed login attempts. Try again later." if is_locked else "Incorrect code." ) ] } def test_cannot_deactivate_totp(auth_client, user_with_totp, user_password): with patch( "allauth.mfa.adapter.DefaultMFAAdapter.can_delete_authenticator" ) as cda_mock: cda_mock.return_value = False resp = auth_client.get(reverse("mfa_deactivate_totp")) assert resp.status_code == HTTPStatus.FOUND assert resp["location"].startswith(reverse("account_reauthenticate")) resp = auth_client.post(resp["location"], {"password": user_password}) assert resp.status_code == HTTPStatus.FOUND resp = auth_client.get(reverse("mfa_deactivate_totp")) # When we GET, the form validation error is already on screen assert resp.context["form"].errors == { "__all__": [get_adapter().error_messages["cannot_delete_authenticator"]], } # And, when we POST anyway, it does not work resp = auth_client.post(reverse("mfa_deactivate_totp")) assert resp.status_code == HTTPStatus.OK assert resp.context["form"].errors == { "__all__": [get_adapter().error_messages["cannot_delete_authenticator"]], } def test_totp_code_reuse( user_with_totp, user_password, totp_validation_bypass, enable_cache ): for code, time_lapse, expect_success in [ # First use of code, SUCCESS ("123", False, True), # Second use, no time elapsed: FAIL ("123", False, False), # Different code, no time elapsed: SUCCESS ("456", False, True), # Again, previous code, no time elapsed: FAIL ("123", False, False), # Previous code, but time elapsed: SUCCESS ("123", True, True), ]: if time_lapse: cache.clear() client = Client() resp = client.post( reverse("account_login"), {"login": user_with_totp.username, "password": user_password}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("mfa_authenticate") # Note that this bypass only bypasses the actual code check, not the # re-use check we're testing here. with totp_validation_bypass(): resp = client.post( reverse("mfa_authenticate"), {"code": code}, ) if expect_success: assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == settings.LOGIN_REDIRECT_URL else: assert resp.status_code == HTTPStatus.OK assert resp.context["form"].errors == { "code": [get_adapter().error_messages["incorrect_code"]] } def test_totp_stage_expires(client, user_with_totp, user_password): resp = client.post( reverse("account_login"), {"login": user_with_totp.username, "password": user_password}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("mfa_authenticate") resp = client.get(reverse("mfa_authenticate")) assert resp.status_code == HTTPStatus.OK assertTemplateUsed(resp, "mfa/authenticate.html") with patch( "allauth.account.internal.stagekit.time.time", return_value=time.time() + 1.1 * app_settings.LOGIN_TIMEOUT, ): resp = client.get(reverse("mfa_authenticate")) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_login") ================================================ FILE: tests/apps/mfa/webauthn/__init__.py ================================================ ================================================ FILE: tests/apps/mfa/webauthn/test_views.py ================================================ from http import HTTPStatus from unittest.mock import ANY from django.conf import settings from django.contrib.auth import get_user_model from django.urls import reverse import pytest from pytest_django.asserts import assertTemplateUsed from allauth.account.authentication import AUTHENTICATION_METHODS_SESSION_KEY from allauth.mfa.models import Authenticator def test_passkey_login(client, passkey, webauthn_authentication_bypass): with webauthn_authentication_bypass(passkey) as credential: resp = client.get( reverse("mfa_login_webauthn"), HTTP_X_REQUESTED_WITH="XMLHttpRequest" ) assert "request_options" in resp.json() resp = client.post( reverse("mfa_login_webauthn"), data={"credential": credential} ) assert resp["location"] == settings.LOGIN_REDIRECT_URL assert client.session[AUTHENTICATION_METHODS_SESSION_KEY] == [ { "at": ANY, "id": ANY, "method": "mfa", "passwordless": True, "type": "webauthn", } ] def test_reauthenticate( auth_client, passkey, user_with_recovery_codes, webauthn_authentication_bypass ): resp = auth_client.get(reverse("mfa_view_recovery_codes")) assert resp.status_code == HTTPStatus.FOUND assert resp["location"].startswith(reverse("account_reauthenticate")) resp = auth_client.get(reverse("mfa_reauthenticate")) assertTemplateUsed(resp, "mfa/reauthenticate.html") with webauthn_authentication_bypass(passkey) as credential: resp = auth_client.get( reverse("mfa_reauthenticate_webauthn"), ) resp = auth_client.post( reverse("mfa_reauthenticate_webauthn"), data={"credential": credential, "next": "/redir"}, ) assert resp["location"] == "/redir" def test_get_passkey_login_challenge_redirects_if_not_ajax(client): resp = client.get(reverse("mfa_login_webauthn")) assert resp["location"] == reverse("account_login") def test_get_passkey_login_challenge(client, db): resp = client.get( reverse("mfa_login_webauthn"), HTTP_X_REQUESTED_WITH="XMLHttpRequest" ) assert resp.status_code == HTTPStatus.OK assert resp["content-type"] == "application/json" data = resp.json() assert data == { "request_options": { "publicKey": { "challenge": ANY, "rpId": "testserver", "allowCredentials": [], "userVerification": "preferred", } } } def test_invalid_passkey_login(client, passkey): resp = client.post(reverse("mfa_login_webauthn"), data={"credential": "{}"}) assert resp["location"] == reverse("account_login") def test_rename_key(auth_client, passkey, reauthentication_bypass): resp = auth_client.get(reverse("mfa_edit_webauthn", kwargs={"pk": passkey.pk})) assert resp["location"].startswith(reverse("account_reauthenticate")) with reauthentication_bypass(): resp = auth_client.get(reverse("mfa_edit_webauthn", kwargs={"pk": passkey.pk})) assertTemplateUsed(resp, "mfa/webauthn/edit_form.html") resp = auth_client.post( reverse("mfa_edit_webauthn", kwargs={"pk": passkey.pk}), data={"name": "Renamed"}, ) assert resp["location"] == reverse("mfa_list_webauthn") passkey.refresh_from_db() assert passkey.data["name"] == "Renamed" assert str(passkey) == "Renamed" def test_remove_key(auth_client, passkey, reauthentication_bypass): resp = auth_client.get(reverse("mfa_remove_webauthn", kwargs={"pk": passkey.pk})) assert resp["location"].startswith(reverse("account_reauthenticate")) with reauthentication_bypass(): resp = auth_client.get( reverse("mfa_remove_webauthn", kwargs={"pk": passkey.pk}) ) assertTemplateUsed(resp, "mfa/webauthn/authenticator_confirm_delete.html") resp = auth_client.post( reverse("mfa_remove_webauthn", kwargs={"pk": passkey.pk}) ) assert resp["location"] == reverse("mfa_list_webauthn") @pytest.mark.parametrize("passwordless", [False, True]) def test_add_key( auth_client, user, webauthn_registration_bypass, reauthentication_bypass, passwordless, ): with webauthn_registration_bypass(user, passwordless) as credential: resp = auth_client.post( reverse("mfa_add_webauthn"), data={"credential": credential} ) assert resp["location"].startswith(reverse("account_reauthenticate")) with reauthentication_bypass(): resp = auth_client.get(reverse("mfa_add_webauthn")) assertTemplateUsed(resp, "mfa/webauthn/add_form.html") with webauthn_registration_bypass(user, passwordless) as credential: resp = auth_client.post( reverse("mfa_add_webauthn"), data={ "credential": credential, "passwordless": "on" if passwordless else "", }, ) assert resp["location"].startswith(reverse("mfa_view_recovery_codes")) authenticator = Authenticator.objects.get( user=user, type=Authenticator.Type.WEBAUTHN ) assert authenticator.wrap().is_passwordless == passwordless Authenticator.objects.filter( user=user, type=Authenticator.Type.RECOVERY_CODES ).exists() def test_list_keys(auth_client): resp = auth_client.get(reverse("mfa_list_webauthn")) assertTemplateUsed(resp, "mfa/webauthn/authenticator_list.html") @pytest.mark.parametrize("email_verified", [False]) @pytest.mark.parametrize("method", ["get", "post"]) @pytest.mark.parametrize("allowed", [True, False]) def test_add_with_unverified_email( auth_client, user, webauthn_registration_bypass, reauthentication_bypass, method, allowed, settings, ): settings.MFA_ALLOW_UNVERIFIED_EMAIL = allowed with webauthn_registration_bypass(user, False) as credential: if method == "get": resp = auth_client.get(reverse("mfa_add_webauthn")) else: resp = auth_client.post( reverse("mfa_add_webauthn"), data={"credential": credential} ) if allowed: assert resp["location"].startswith(reverse("account_reauthenticate")) else: assert resp["location"] == reverse("mfa_index") def test_passkey_signup(client, db, webauthn_registration_bypass): resp = client.post( reverse("account_signup_by_passkey"), data={"email": "pass@key.org", "username": "passkey"}, ) assert resp["location"] == reverse("mfa_signup_webauthn") resp = client.post(resp["location"]) assert resp.status_code == HTTPStatus.OK user = get_user_model().objects.get(email="pass@key.org") with webauthn_registration_bypass(user, True) as credential: resp = client.post( reverse("mfa_signup_webauthn"), data={"credential": credential} ) assert resp["location"] == settings.LOGIN_REDIRECT_URL def test_webauthn_login( client, user_with_passkey, passkey, user_password, webauthn_authentication_bypass ): resp = client.post( reverse("account_login"), {"login": user_with_passkey.username, "password": user_password}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("mfa_authenticate") with webauthn_authentication_bypass(passkey) as credential: resp = client.get(reverse("mfa_authenticate")) assert resp.status_code == HTTPStatus.OK resp = client.post(reverse("mfa_authenticate"), {"credential": credential}) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == settings.LOGIN_REDIRECT_URL assert client.session[AUTHENTICATION_METHODS_SESSION_KEY] == [ {"method": "password", "at": ANY, "username": user_with_passkey.username}, {"method": "mfa", "at": ANY, "id": ANY, "type": Authenticator.Type.WEBAUTHN}, ] ================================================ FILE: tests/apps/socialaccount/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/base.py ================================================ import base64 import hashlib import json import random import requests import uuid import warnings from http import HTTPStatus from urllib.parse import parse_qs, urlparse from django.conf import settings from django.contrib.auth import get_user_model from django.test import RequestFactory, TestCase from django.test.utils import override_settings from django.urls import reverse from django.utils.http import urlencode import jwt import allauth.app_settings from allauth.account.models import EmailAddress from allauth.account.utils import user_email, user_username from allauth.socialaccount import app_settings from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.models import SocialAccount, SocialApp from tests.mocking import MockedResponse, mocked_response def setup_app(provider_id): request = RequestFactory().get("/") apps = get_adapter().list_apps(request, provider_id) if apps: return apps[0] app = SocialApp.objects.create( provider=provider_id, name=provider_id, client_id="app123id", key=provider_id, secret="dummy", ) if allauth.app_settings.SITES_ENABLED: from django.contrib.sites.models import Site app.sites.add(Site.objects.get_current()) return app class OAuthTestsMixin: provider_id: str def get_mocked_response(self): pass def get_expected_to_str(self): raise NotImplementedError def setUp(self): super().setUp() self.app = setup_app(self.provider_id) request = RequestFactory().get("/") self.provider = self.app.get_provider(request) @override_settings(SOCIALACCOUNT_AUTO_SIGNUP=False) def test_login(self): resp_mocks = self.get_mocked_response() if resp_mocks is None: warnings.warn(f"Cannot test provider {self.provider.id}, no oauth mock") return resp = self.login(resp_mocks) self.assertRedirects(resp, reverse("socialaccount_signup")) resp = self.client.get(reverse("socialaccount_signup")) sociallogin = resp.context["form"].sociallogin data = dict( email=user_email(sociallogin.user), username=str(random.randrange(1000, 10000000)), ) resp = self.client.post(reverse("socialaccount_signup"), data=data) self.assertRedirects(resp, "/accounts/profile/", fetch_redirect_response=False) user = resp.context["user"] self.assertFalse(user.has_usable_password()) account = SocialAccount.objects.get(user=user, provider=self.provider.id) provider_account = account.get_provider_account() self.assertEqual(provider_account.to_str(), self.get_expected_to_str()) # The following lines don't actually test that much, but at least # we make sure that the code is hit. provider_account.get_avatar_url() provider_account.get_profile_url() provider_account.get_brand() @override_settings( SOCIALACCOUNT_AUTO_SIGNUP=True, SOCIALACCOUNT_EMAIL_REQUIRED=False, ACCOUNT_EMAIL_REQUIRED=False, ) def test_auto_signup(self): resp_mocks = self.get_mocked_response() if not resp_mocks: warnings.warn(f"Cannot test provider {self.provider.id}, no oauth mock") return resp = self.login(resp_mocks) self.assertRedirects(resp, "/accounts/profile/", fetch_redirect_response=False) self.assertFalse(resp.context["user"].has_usable_password()) def login(self, resp_mocks, process="login"): with mocked_response( MockedResponse( HTTPStatus.OK, "oauth_token=token&oauth_token_secret=psst", {"content-type": "text/html"}, ) ): resp = self.client.post( reverse(f"{self.provider.id}_login") + "?" + urlencode(dict(process=process)) ) p = urlparse(resp["location"]) q = parse_qs(p.query) complete_url = reverse(f"{self.provider.id}_callback") self.assertGreater(q["oauth_callback"][0].find(complete_url), 0) with mocked_response(self.get_access_token_response(), *resp_mocks): resp = self.client.get(complete_url) return resp def get_access_token_response(self): return MockedResponse( HTTPStatus.OK, "oauth_token=token&oauth_token_secret=psst", {"content-type": "text/html"}, ) def test_authentication_error(self): resp = self.client.get(reverse(f"{self.provider.id}_callback")) assert resp.status_code == HTTPStatus.UNAUTHORIZED template_ext = getattr(settings, "ACCOUNT_TEMPLATE_EXTENSION", "html") self.assertTemplateUsed( resp, f"socialaccount/authentication_error.{template_ext}" ) # For backward-compatibility with third-party provider tests that call # create_oauth_tests() rather than using the mixin directly. def create_oauth_tests(provider): class Class(OAuthTestsMixin, TestCase): provider_id = provider.id Class.__name__ = f"OAuthTests_{provider.id}" return Class class OAuth2TestsMixin: provider_id: str def get_mocked_response(self): pass def get_expected_to_str(self): raise NotImplementedError def get_access_token(self) -> str: return "testac" def get_refresh_token(self) -> str: return "testrf" def get_login_response_json(self, with_refresh_token=True): response = { "uid": uuid.uuid4().hex, "access_token": self.get_access_token(), } if with_refresh_token: response["refresh_token"] = self.get_refresh_token() return json.dumps(response) def mocked_response(self, *responses): return mocked_response(*responses) def setUp(self): super().setUp() self.setup_provider() def setup_provider(self): self.app = setup_app(self.provider_id) self.request = RequestFactory().get("/") self.provider = self.app.get_provider(self.request) def test_provider_has_no_pkce_params(self): provider_settings = app_settings.PROVIDERS.get(self.app.provider, {}) provider_settings_with_pkce_set = provider_settings.copy() provider_settings_with_pkce_set["OAUTH_PKCE_ENABLED"] = False with self.settings( SOCIALACCOUNT_PROVIDERS={self.app.provider: provider_settings_with_pkce_set} ): self.assertEqual(self.provider.get_pkce_params(), {}) def test_provider_has_pkce_params(self): provider_settings = app_settings.PROVIDERS.get(self.app.provider, {}) provider_settings_with_pkce_set = provider_settings.copy() provider_settings_with_pkce_set["OAUTH_PKCE_ENABLED"] = True with self.settings( SOCIALACCOUNT_PROVIDERS={self.app.provider: provider_settings_with_pkce_set} ): pkce_params = self.provider.get_pkce_params() self.assertEqual( set(pkce_params.keys()), {"code_challenge", "code_challenge_method", "code_verifier"}, ) hashed_verifier = hashlib.sha256( pkce_params["code_verifier"].encode("ascii") ) code_challenge = base64.urlsafe_b64encode(hashed_verifier.digest()) code_challenge_without_padding = code_challenge.rstrip(b"=") assert pkce_params["code_challenge"] == code_challenge_without_padding @override_settings(SOCIALACCOUNT_AUTO_SIGNUP=False) def test_login(self): resp_mock = self.get_mocked_response() if not resp_mock: warnings.warn(f"Cannot test provider {self.provider.id}, no oauth mock") return resp = self.login( resp_mock, ) self.assertRedirects(resp, reverse("socialaccount_signup")) @override_settings(SOCIALACCOUNT_AUTO_SIGNUP=False) def test_login_with_pkce_disabled(self): provider_settings = app_settings.PROVIDERS.get(self.app.provider, {}) provider_settings_with_pkce_disabled = provider_settings.copy() provider_settings_with_pkce_disabled["OAUTH_PKCE_ENABLED"] = False with self.settings( SOCIALACCOUNT_PROVIDERS={ self.app.provider: provider_settings_with_pkce_disabled } ): resp_mock = self.get_mocked_response() if not resp_mock: warnings.warn(f"Cannot test provider {self.provider.id}, no oauth mock") return resp = self.login( resp_mock, ) self.assertRedirects(resp, reverse("socialaccount_signup")) @override_settings(SOCIALACCOUNT_AUTO_SIGNUP=False) def test_login_with_pkce_enabled(self): provider_settings = app_settings.PROVIDERS.get(self.app.provider, {}) provider_settings_with_pkce_enabled = provider_settings.copy() provider_settings_with_pkce_enabled["OAUTH_PKCE_ENABLED"] = True with self.settings( SOCIALACCOUNT_PROVIDERS={ self.app.provider: provider_settings_with_pkce_enabled } ): resp_mock = self.get_mocked_response() if not resp_mock: warnings.warn(f"Cannot test provider {self.provider.id}, no oauth mock") return resp = self.login( resp_mock, ) self.assertRedirects(resp, reverse("socialaccount_signup")) @override_settings(SOCIALACCOUNT_STORE_TOKENS=True) def test_account_tokens(self, multiple_login=False): email = "user@example.com" user = get_user_model()(is_active=True) user_email(user, email) user_username(user, "user") user.set_password("test") user.save() EmailAddress.objects.create(user=user, email=email, primary=True, verified=True) self.client.login(username=user.username, password="test") self.login(self.get_mocked_response(), process="connect") if multiple_login: self.login( self.get_mocked_response(), with_refresh_token=False, process="connect", ) # get account sa = SocialAccount.objects.filter( user=user, provider=self.provider.app.provider_id or self.provider.id ).get() provider_account = sa.get_provider_account() self.assertEqual(provider_account.to_str(), self.get_expected_to_str()) # The following lines don't actually test that much, but at least # we make sure that the code is hit. provider_account.get_avatar_url() provider_account.get_profile_url() provider_account.get_brand() # get token if self.app: t = sa.socialtoken_set.get() # verify access_token and refresh_token self.assertEqual(self.get_access_token(), t.token) resp = json.loads(self.get_login_response_json(with_refresh_token=True)) if "refresh_token" in resp: refresh_token = resp.get("refresh_token") elif "refreshToken" in resp: refresh_token = resp.get("refreshToken") else: refresh_token = "" self.assertEqual(t.token_secret, refresh_token) @override_settings(SOCIALACCOUNT_STORE_TOKENS=True) def test_account_refresh_token_saved_next_login(self): """ fails if a login missing a refresh token, deletes the previously saved refresh token. Systems such as google's oauth only send a refresh token on first login. """ self.test_account_tokens(multiple_login=True) def login(self, resp_mock=None, process="login", with_refresh_token=True): with self.mocked_response(): resp = self.client.post( self.provider.get_login_url(self.request, process=process) ) p = urlparse(resp["location"]) q = parse_qs(p.query) pkce_enabled = app_settings.PROVIDERS.get(self.app.provider, {}).get( "OAUTH_PKCE_ENABLED", self.provider.pkce_enabled_default ) self.assertEqual("code_challenge" in q, pkce_enabled) self.assertEqual("code_challenge_method" in q, pkce_enabled) if pkce_enabled: code_challenge = q["code_challenge"][0] self.assertEqual(q["code_challenge_method"][0], "S256") complete_url = self.provider.get_callback_url() self.assertGreater(q["redirect_uri"][0].find(complete_url), 0) response_json = self.get_login_response_json( with_refresh_token=with_refresh_token ) if isinstance(resp_mock, list): resp_mocks = resp_mock elif resp_mock is None: resp_mocks = [] else: resp_mocks = [resp_mock] with self.mocked_response( MockedResponse( HTTPStatus.OK, response_json, {"content-type": "application/json"} ), *resp_mocks, ): resp = self.client.get(complete_url, self.get_complete_parameters(q)) # Find the access token POST request, and assert that it contains # the correct code_verifier if and only if PKCE is enabled request_calls = requests.Session.request.call_args_list for args, kwargs in request_calls: data = kwargs.get("data", {}) if ( args[0] == "POST" and isinstance(data, dict) and data.get("redirect_uri", "").endswith(complete_url) ): self.assertEqual("code_verifier" in data, pkce_enabled) if pkce_enabled: hashed_code_verifier = hashlib.sha256( data["code_verifier"].encode("ascii") ) expected_code_challenge = ( base64.urlsafe_b64encode(hashed_code_verifier.digest()) .rstrip(b"=") .decode() ) self.assertEqual(code_challenge, expected_code_challenge) return resp def get_complete_parameters(self, q): return {"code": "test", "state": q["state"][0]} def test_authentication_error(self): resp = self.client.get(self.provider.get_callback_url()) template_ext = getattr(settings, "ACCOUNT_TEMPLATE_EXTENSION", "html") self.assertTemplateUsed( resp, f"socialaccount/authentication_error.{template_ext}" ) class OpenIDConnectTests(OAuth2TestsMixin): oidc_info_content = { "authorization_endpoint": "/login", "userinfo_endpoint": "/userinfo", "token_endpoint": "/token", "jwks_uri": "/jwks", "issuer": "https://issuer.example.com", } userinfo_content = { "picture": "https://secure.gravatar.com/avatar/123", "email": "ness@some.oidc.server.onett.example", "sub": 2187, "identities": [], "name": "Ness", } id_token = { "email": "ness@some.oidc.server.onett.example", "sub": 2187, "preferred_username": "wizard", "iss": "https://issuer.example.com", "aud": "app123id", } def get_id_token(self) -> dict: return self.id_token def get_login_response_json(self, with_refresh_token=True) -> str: data = json.loads( super().get_login_response_json(with_refresh_token=with_refresh_token) ) data["id_token"] = jwt.encode(self.get_id_token(), "secret") return json.dumps(data) def mocked_response(self, *responses): return mocked_response(*responses, callback=self._mocked_responses) def get_expected_to_str(self): return "ness@some.oidc.server.onett.example" def setup_provider(self): self.app = setup_app(self.provider_id) self.app.provider_id = self.provider_id self.app.provider = "openid_connect" self.app.settings = { "server_url": "https://unittest.example.com", } self.app.save() self.request = RequestFactory().get("/") self.provider = self.app.get_provider(self.request) def get_mocked_response(self): # Enable test_login in OAuth2TestsMixin, but this response mock is unused return True def _mocked_responses(self, url, *args, **kwargs): if url.endswith("/.well-known/openid-configuration"): return MockedResponse(HTTPStatus.OK, json.dumps(self.oidc_info_content)) elif url.endswith("/userinfo"): return MockedResponse(HTTPStatus.OK, json.dumps(self.userinfo_content)) @override_settings(SOCIALACCOUNT_AUTO_SIGNUP=True) def test_login_auto_signup(self): resp = self.login() self.assertRedirects(resp, "/accounts/profile/", fetch_redirect_response=False) sa = SocialAccount.objects.get(provider=self.app.provider_id) expected_data = {"id_token": self.id_token} if self.app.settings.get("fetch_userinfo", True): expected_data["userinfo"] = self.userinfo_content self.assertDictEqual(sa.extra_data, expected_data) def test_404_on_unknown_provider_id(self): """ Make sure that OIDC endpoints hit with an invalid provider_id not corresponding to any configured social "apps" returns a 404 instead of an unhandled SocialApp.DoesNotExist. """ # we can't use self.provider.get_login_url as we intentionally # do not want to use the configured provider's ID, so let's inline # OpenIDConnectProvider.get_login_url login_url = reverse( f"{self.app.provider}_login", kwargs={ # intentionally invalidate the ID "provider_id": f"{self.app.provider_id}-invalid" }, ) resp = self.client.post(login_url) self.assertEqual(resp.status_code, HTTPStatus.NOT_FOUND) # same with the callback endpoint - inlining OpenIDConnectProvider.get_callback_url callback_url = reverse( f"{self.app.provider}_callback", kwargs={ # intentionally invalidate the ID "provider_id": f"{self.app.provider_id}-invalid" }, ) # note: callback is a GET endpoint resp = self.client.get(callback_url) self.assertEqual(resp.status_code, HTTPStatus.NOT_FOUND) ================================================ FILE: tests/apps/socialaccount/conftest.py ================================================ import time from contextlib import contextmanager from http import HTTPStatus from unittest.mock import patch from django.urls import reverse import pytest from allauth.account.models import EmailAddress from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.internal import statekit from allauth.socialaccount.models import SocialAccount, SocialLogin, SocialToken from allauth.socialaccount.providers.base.constants import AuthProcess from tests.mocking import MockedResponse, mocked_response @pytest.fixture def sociallogin_factory(user_factory): def factory( email=None, username=None, with_email=True, provider="unittest-server", uid="123", email_verified=True, with_token=False, ): user = user_factory( username=username, email=email, commit=False, with_email=with_email ) provider_instance = get_adapter().get_provider(request=None, provider=provider) account = SocialAccount(provider=provider, uid=uid) sociallogin = SocialLogin( provider=provider_instance, user=user, account=account ) if with_email: sociallogin.email_addresses = [ EmailAddress(email=user.email, verified=email_verified, primary=True) ] if with_token: sociallogin.token = SocialToken(token="123", token_secret="456") # nosec return sociallogin return factory @pytest.fixture def jwt_decode_bypass(): @contextmanager def f(jwt_data): with patch("allauth.socialaccount.internal.jwtkit.verify_and_decode") as m: data = { "iss": "https://accounts.google.com", "aud": "client_id", "sub": "123sub", "hd": "example.com", "email": "raymond@example.com", "email_verified": True, "at_hash": "HK6E_P6Dh8Y93mRNtsDB1Q", "name": "Raymond Penners", "picture": "https://lh5.googleusercontent.com/photo.jpg", "given_name": "Raymond", "family_name": "Penners", "locale": "en", "iat": 123, "exp": 456, } data.update(jwt_data) m.return_value = data yield return f @pytest.fixture def provider_callback_response(): def f(client, process=AuthProcess.LOGIN): with mocked_response( { "token_endpoint": "/", "userinfo_endpoint": "/", }, MockedResponse( HTTPStatus.OK, "access_token=456", {"content-type": "dummy"} ), { "sub": "sub123", }, ): session = client.session session[statekit.STATES_SESSION_KEY] = { "state456": [{"process": process}, time.time()] } session.save() resp = client.post( reverse( "openid_connect_callback", kwargs={"provider_id": "unittest-server"}, ) + "?code=123&state=state456" ) return resp return f ================================================ FILE: tests/apps/socialaccount/internal/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/internal/test_jwtkit.py ================================================ from datetime import timedelta from django.utils import timezone from allauth.socialaccount.internal.jwtkit import verify_and_decode from allauth.socialaccount.providers.apple.client import jwt_encode from allauth.socialaccount.providers.oauth2.client import OAuth2Error def test_verify_and_decode(enable_cache): now = timezone.now() payload = { "iss": "https://accounts.google.com", "azp": "client_id", "aud": "client_id", "sub": "108204268033311374519", "hd": "example.com", "locale": "en", "iat": now, "jti": "a4e9b64d5e31da48a2037216e4ba9a5f5f4f50a0", "exp": now + timedelta(hours=1), } id_token = jwt_encode(payload, "secret") for attempt in range(2): try: verify_and_decode( credential=id_token, keys_url="/", issuer=payload["iss"], audience=payload["aud"], lookup_kid=False, verify_signature=False, ) assert attempt == 0 except OAuth2Error: assert attempt == 1 ================================================ FILE: tests/apps/socialaccount/internal/test_statekit.py ================================================ import time from allauth.socialaccount.internal import statekit def test_get_oldest_state(): states = { "new": [{"id": "new"}, 300], "mid": [{"id": "mid"}, 200], "old": [{"id": "old"}, 100], } state_id, state = statekit.get_oldest_state(states) assert state_id == "old" assert state["id"] == "old" def test_get_oldest_state_empty(): state_id, state = statekit.get_oldest_state({}) assert state_id is None assert state is None def test_gc_states(): states = {} for i in range(statekit.MAX_STATES + 1): states[f"state-{i}"] = [{"i": i}, 1000 + i] assert len(states) == statekit.MAX_STATES + 1 statekit.gc_states(states) assert len(states) == statekit.MAX_STATES assert "state-0" not in states def test_stashing(rf): request = rf.get("/") request.session = {} # Stash states with a small delay state_id = statekit.stash_state(request, {"foo": "bar"}) time.sleep(0.001) # delay for microseconds state2_id = statekit.stash_state(request, {"foo2": "bar2"}) time.sleep(0.001) # delay for microseconds state3_id = statekit.stash_state(request, {"foo3": "bar3"}) # Unstash last state and check order state = statekit.unstash_last_state(request) assert state == {"foo3": "bar3"} state = statekit.unstash_state(request, state3_id) assert state is None state = statekit.unstash_state(request, state2_id) assert state == {"foo2": "bar2"} state = statekit.unstash_state(request, state2_id) assert state is None state = statekit.unstash_state(request, state_id) assert state == {"foo": "bar"} state = statekit.unstash_state(request, state_id) assert state is None ================================================ FILE: tests/apps/socialaccount/providers/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/agave/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/agave/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.agave.provider import AgaveProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class AgaveTests(OAuth2TestsMixin, TestCase): provider_id = AgaveProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "status": "success", "message": "User details retrieved successfully.", "version": "2.0.0-SNAPSHOT-rc3fad", "result": { "first_name": "John", "last_name": "Doe", "full_name": "John Doe", "email": "jon@doe.edu", "phone": "", "mobile_phone": "", "status": "Active", "create_time": "20180322043812Z", "username": "jdoe" } } """, ) def get_expected_to_str(self): return "jdoe" ================================================ FILE: tests/apps/socialaccount/providers/amazon/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/amazon/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.amazon.provider import AmazonProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class AmazonTests(OAuth2TestsMixin, TestCase): provider_id = AmazonProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "Profile":{ "CustomerId":"amzn1.account.K2LI23KL2LK2", "Name":"John Doe", "PrimaryEmail":"johndoe@example.com" } }""", ) def get_expected_to_str(self): return "johndoe@example.com" ================================================ FILE: tests/apps/socialaccount/providers/amazon_cognito/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/amazon_cognito/tests.py ================================================ import json from http import HTTPStatus from django.test import TestCase, override_settings import pytest from allauth.account.models import EmailAddress from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.amazon_cognito.provider import ( AmazonCognitoProvider, ) from allauth.socialaccount.providers.amazon_cognito.utils import ( convert_to_python_bool_if_value_is_json_string_bool, ) from allauth.socialaccount.providers.amazon_cognito.views import ( AmazonCognitoOAuth2Adapter, ) from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse def _get_mocked_claims(): return { "sub": "4993b410-8a1b-4c36-b843-a9c1a697e6b7", "given_name": "John", "family_name": "Doe", "email": "jdoe@example.com", "username": "johndoe", } @override_settings( SOCIALACCOUNT_PROVIDERS={ "amazon_cognito": {"DOMAIN": "https://domain.auth.us-east-1.amazoncognito.com"} } ) class AmazonCognitoTestCase(OAuth2TestsMixin, TestCase): provider_id = AmazonCognitoProvider.id def get_mocked_response(self): mocked_payload = json.dumps(_get_mocked_claims()) return MockedResponse(status_code=HTTPStatus.OK, content=mocked_payload) def get_expected_to_str(self): return "johndoe" @override_settings(SOCIALACCOUNT_PROVIDERS={"amazon_cognito": {}}) def test_oauth2_adapter_raises_if_domain_settings_is_missing( self, ): mocked_response = self.get_mocked_response() with self.assertRaises( ValueError, msg=AmazonCognitoOAuth2Adapter.DOMAIN_KEY_MISSING_ERROR, ): self.login(mocked_response) def test_saves_email_as_verified_if_email_is_verified_in_cognito( self, ): mocked_claims = _get_mocked_claims() mocked_claims["email_verified"] = True mocked_payload = json.dumps(mocked_claims) mocked_response = MockedResponse( status_code=HTTPStatus.OK, content=mocked_payload ) self.login(mocked_response) user_id = SocialAccount.objects.get(uid=mocked_claims["sub"]).user_id email_address = EmailAddress.objects.get(user_id=user_id) self.assertEqual(email_address.email, mocked_claims["email"]) self.assertTrue(email_address.verified) def test_provider_slug_replaces_underscores_with_hyphens(self): self.assertTrue("_" not in self.provider.get_slug()) @pytest.mark.parametrize( "input,output", [ (True, True), ("true", True), ("false", False), (False, False), ], ) def test_convert_bool(input, output): assert convert_to_python_bool_if_value_is_json_string_bool(input) == output ================================================ FILE: tests/apps/socialaccount/providers/angellist/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/angellist/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.angellist.provider import AngelListProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class AngelListTests(OAuth2TestsMixin, TestCase): provider_id = AngelListProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ {"name":"pennersr","id":424732,"bio":"","follower_count":0, "angellist_url":"https://angel.co/dsxtst", "image":"https://angel.co/images/shared/nopic.png", "email":"raymond.penners@example.com","blog_url":null, "online_bio_url":null,"twitter_url":"https://x.com/dsxtst", "facebook_url":null,"linkedin_url":null,"aboutme_url":null, "github_url":null,"dribbble_url":null,"behance_url":null, "what_ive_built":null,"locations":[],"roles":[],"skills":[], "investor":false,"scopes":["message","talent","dealflow","comment", "email"]} """, ) def get_expected_to_str(self): return "raymond.penners@example.com" ================================================ FILE: tests/apps/socialaccount/providers/apple/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/apple/tests.py ================================================ import json import time from http import HTTPStatus from importlib import import_module from urllib.parse import parse_qs, urlparse from django.conf import settings from django.test import TestCase from django.test.utils import override_settings from django.urls import reverse from django.utils.http import urlencode import jwt from allauth.socialaccount.providers.apple.apple_session import ( APPLE_SESSION_COOKIE_NAME, ) from allauth.socialaccount.providers.apple.client import jwt_encode from allauth.socialaccount.providers.apple.provider import AppleProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse, mocked_response # Generated on https://mkjwk.org/, used to sign and verify the apple id_token TESTING_JWT_KEYSET = { "p": ( "4ADzS5jKx_kdQihyOocVS0Qwwo7m0f7Ow56EadySJ-cmnwoHHF3AxgRaq-h-KwybSphv" "dc-X7NbS79-b9dumHKyt1MeVLAsDZD1a-uQCEneY1g9LsQkscNr7OggcpvMg5UUFwv6A" "kavu8cB0iyhNdha5_AWX27K5lNebvpaXEJ8" ), "kty": "RSA", "q": ( "yy5UvMjrvZyO1Os_nxXIugCa3NyWOkC8oMppPvr1Bl5AnF_xwXN2n9ozPd9Nb3Q3n-om" "NgLayyUxhwIjWDlI67Vbx-ESuff8ZEBKuTK0Gdmr4C_QU_j0gvvNMNJweSPxDdRmIUgO" "njTVNWmdqFTZs43jXAT4J519rgveNLAkGNE" ), "d": ( "riPuGIDde88WS03CVbo_mZ9toFWPyTxvuz8VInJ9S1ZxULo-hQWDBohWGYwvg8cgfXck" "cqWt5OBqNvPYdLgwb84uVi2JeEHmhcQSc_x0zfRTau5HVE2KdR-gWxQjPWoaBHeDVqwo" "PKaU2XYxa-gYDXcuSJWHz3BX13oInDEFCXr6VwiLiwLBFsb63EEHwyWXJbTpoar7AARW" "kz76qtngDkk4t9gk_Q0L1y1qf1GeWiAL7xWb-bdptma4-1ui-R2219-1ONEZ41v_jsIS" "_z8ooXmVCbUsHV4Z1UDpRvpORVE3u57WK3qXUdAtZsXjaIwkdItbDmL1jFUgefwfO91Y" "YQ" ), "e": "AQAB", "use": "sig", "kid": "testkey", "qi": ( "R0Hu4YmpHzw3SKWGYuAcAo6B97-JlN2fXiTjZ2g8eHGQX7LSoKEu0Hmu5hcBZYSgOuor" "IPsPUu3mNtx3pjLMOaJRk34VwcYu7h23ogEKGcPUt1c4tTotFDdw8WFptDOw4ow31Tml" "BPExLqzzGjJeQSNULB1bExuuhYMWx6wBXo8" ), "dp": ( "WBaHlnbjZ3hDVTzqjrGIYizSr-_aPUJitPKlR6wBncd8nJYo7bLAmB4mOewXkX5HozIG" "wuF78RsZoFLi1fAmhqgxQ7eopcU-9DBcksUPO4vkgmlJbrkYzNiQauW9vrllekOGXIQQ" "szhVoqP4MLEMpR-Sy9S3PyItcKbJDE3T4ik" ), "alg": "RS256", "dq": ( "Ar5kbIw2CsBzeVKX8FkF9eUOMk9URAMdyPoSw8P1zRk2vCXbiOY7Qttad8ptLEUgfytV" "SsNtGvMsoQsZWRak8nHnhGJ4s0QzB1OK7sdNgU_cL1HV-VxSSPaHhdJBrJEcrzggDPEB" "KYfDHU6Iz34d1nvjBxoWE8rfqJsGbCW4xxE" ), "n": ( "sclLPioUv4VOcOZWAKoRhcvwIH2jOhoHhSI_Cj5c5zSp7qaK8jCU6T7-GObsgrhpty-k" "26ZuqRdgu9d-62WO8OBGt1e0wxbTh128-nTTrOESHUlV_K1wpJmXOxNpJiybcgzZNbAm" "ACmsHfxZvN9bt7gKPXxf3-_zFAf12PbYMrOionAJ1N_4HxL7fz3xkr5C87Av06QNilIC" "-mA-4n9Eqw_R2DYNpE3RYMdWtwKqBwJC8qs3677RpG9vcc-yZ_97pEiytd2FBJ8uoTwH" "d3DHJB8UVgBSh1kMUpSdoM7HxVzKx732nx6Kusln79LrsfOzrXF4enkfKJYI40-uwT95" "zw" ), } # Mocked version of the test data from https://appleid.apple.com/auth/keys KEY_SERVER_RESP_JSON = json.dumps( { "keys": [ { "kty": TESTING_JWT_KEYSET["kty"], "kid": TESTING_JWT_KEYSET["kid"], "use": TESTING_JWT_KEYSET["use"], "alg": TESTING_JWT_KEYSET["alg"], "n": TESTING_JWT_KEYSET["n"], "e": TESTING_JWT_KEYSET["e"], } ] } ) def sign_id_token(payload): """ Sign a payload as apple normally would for the id_token. """ signing_key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(TESTING_JWT_KEYSET)) return jwt_encode( payload, signing_key, algorithm="RS256", headers={"kid": TESTING_JWT_KEYSET["kid"]}, ) @override_settings( SOCIALACCOUNT_STORE_TOKENS=False, SOCIALACCOUNT_PROVIDERS={ "apple": { "APP": { "client_id": "app123id", "key": "apple", "secret": "dummy", "settings": { "certificate_key": """-----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg2+Eybl8ojH4wB30C 3/iDkpsrxuPfs3DZ+3nHNghBOpmhRANCAAQSpo1eQ+EpNgQQyQVs/F27dkq3gvAI 28m95JEk26v64YAea5NTH56mru30RDqTKPgRVi5qRu3XGyqy3mdb8gMy -----END PRIVATE KEY----- """, }, } } }, ) class AppleTests(OAuth2TestsMixin, TestCase): provider_id = AppleProvider.id def get_apple_id_token_payload(self): now = int(time.time()) return { "iss": "https://appleid.apple.com", "aud": "app123id", # Matches `setup_app` "exp": now + 60 * 60, "iat": now, "sub": "000313.c9720f41e9434e18987a.1218", "at_hash": "CkaUPjk4MJinaAq6Z0tGUA", "email": "test@privaterelay.appleid.com", "email_verified": "true", "is_private_email": "true", "auth_time": 1234345345, # not converted automatically by pyjwt } def test_verify_token(self): id_token = sign_id_token(self.get_apple_id_token_payload()) with mocked_response(self.get_mocked_response()): sociallogin = self.provider.verify_token(None, {"id_token": id_token}) assert sociallogin.user.email == "test@privaterelay.appleid.com" def get_login_response_json(self, with_refresh_token=True): """ `with_refresh_token` is not optional for apple, so it's ignored. """ id_token = sign_id_token(self.get_apple_id_token_payload()) return json.dumps( { "access_token": "testac", # Matches OAuth2TestsMixin value "expires_in": 3600, "id_token": id_token, "refresh_token": "testrt", # Matches OAuth2TestsMixin value "token_type": "Bearer", } ) def get_mocked_response(self): """ Apple is unusual in that the `id_token` contains all the user info so no profile info request is made. However, it does need the public key verification, so this mocked response is the public key request in order to verify the authenticity of the id_token. """ return MockedResponse( HTTPStatus.OK, KEY_SERVER_RESP_JSON, {"content-type": "application/json"} ) def get_expected_to_str(self): return "A B" def get_complete_parameters(self, auth_request_params): """ Add apple specific response parameters which they include in the form_post response. https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js/incorporating_sign_in_with_apple_into_other_platforms """ params = super().get_complete_parameters(auth_request_params) params.update( { "id_token": sign_id_token(self.get_apple_id_token_payload()), "user": json.dumps( { "email": "private@appleid.apple.com", "name": { "firstName": "A", "lastName": "B", }, } ), } ) return params def login(self, resp_mock, process="login", with_refresh_token=True): resp = self.client.post( f"{reverse(f'{self.provider.id}_login')}?{urlencode(dict(process=process))}" ) p = urlparse(resp["location"]) q = parse_qs(p.query) complete_url = reverse(f"{self.provider.id}_callback") self.assertGreater(q["redirect_uri"][0].find(complete_url), 0) response_json = self.get_login_response_json( with_refresh_token=with_refresh_token ) with mocked_response( MockedResponse( HTTPStatus.OK, response_json, {"content-type": "application/json"} ), resp_mock, ): resp = self.client.post( complete_url, data=self.get_complete_parameters(q), ) assert reverse("apple_finish_callback") in resp.url # Follow the redirect resp = self.client.get(resp.url) return resp def test_authentication_error(self): """Override base test because apple posts errors""" resp = self.client.post( reverse(f"{self.provider.id}_callback"), data={"error": "misc", "state": "testingstate123"}, ) assert reverse("apple_finish_callback") in resp.url # Follow the redirect resp = self.client.get(resp.url) template_ext = getattr(settings, "ACCOUNT_TEMPLATE_EXTENSION", "html") self.assertTemplateUsed( resp, f"socialaccount/authentication_error.{template_ext}" ) def test_apple_finish(self): resp = self.login(self.get_mocked_response()) # Check request generating the response finish_url = reverse("apple_finish_callback") self.assertEqual(resp.request["PATH_INFO"], finish_url) self.assertTrue("state" in resp.request["QUERY_STRING"]) self.assertTrue("code" in resp.request["QUERY_STRING"]) # Check have cookie containing apple session self.assertTrue(APPLE_SESSION_COOKIE_NAME in self.client.cookies) # Session should have been cleared apple_session_cookie = self.client.cookies.get(APPLE_SESSION_COOKIE_NAME) engine = import_module(settings.SESSION_ENGINE) SessionStore = engine.SessionStore apple_login_session = SessionStore(apple_session_cookie.value) self.assertEqual(len(apple_login_session.keys()), 0) # Check cookie path was correctly set self.assertEqual(apple_session_cookie.get("path"), finish_url) ================================================ FILE: tests/apps/socialaccount/providers/asana/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/asana/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.asana.provider import AsanaProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class AsanaTests(OAuth2TestsMixin, TestCase): provider_id = AsanaProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ {"data": {"photo": null, "workspaces": [{"id": 31337, "name": "example.com"}, {"id": 3133777, "name": "Personal Projects"}], "email": "test@example.com", "name": "Test Name", "gid": 43748387}}""", ) def get_expected_to_str(self): return "test@example.com" ================================================ FILE: tests/apps/socialaccount/providers/atlassian/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/atlassian/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.atlassian.provider import AtlassianProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class AtlassianTests(OAuth2TestsMixin, TestCase): provider_id = AtlassianProvider.id def get_mocked_response(self): response_data = """ { "account_type": "atlassian", "account_id": "112233aa-bb11-cc22-33dd-445566abcabc", "email": "mia@example.com", "email_verified": true, "name": "Mia Krystof", "picture": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/112233aa-bb11-cc22-33dd-445566abcabc/1234abcd-9876-54aa-33aa-1234dfsade9487ds", "account_status": "active", "nickname": "mkrystof", "zoneinfo": "Australia/Sydney", "locale": "en-US", "extended_profile": { "job_title": "Designer", "organization": "mia@example.com", "department": "Design team", "location": "Sydney" } }""" return MockedResponse(HTTPStatus.OK, response_data) def get_expected_to_str(self): return "mia@example.com" ================================================ FILE: tests/apps/socialaccount/providers/auth0/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/auth0/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.auth0.provider import Auth0Provider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class Auth0Tests(OAuth2TestsMixin, TestCase): provider_id = Auth0Provider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "picture": "https://secure.gravatar.com/avatar/123", "email": "mr.bob@your.Auth0.server.example.com", "id": 2, "sub": 2, "identities": [], "name": "Mr Bob" } """, ) def get_expected_to_str(self): return "mr.bob@your.Auth0.server.example.com" ================================================ FILE: tests/apps/socialaccount/providers/authentiq/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/authentiq/tests.py ================================================ import json from http import HTTPStatus from django.test import TestCase from django.test.client import RequestFactory from django.test.utils import override_settings from allauth.socialaccount.providers.authentiq.provider import AuthentiqProvider from allauth.socialaccount.providers.authentiq.views import AuthentiqOAuth2Adapter from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class AuthentiqTests(OAuth2TestsMixin, TestCase): provider_id = AuthentiqProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, json.dumps( { "sub": "ZLARGMFT1M", "email": "jane@email.invalid", "email_verified": True, "given_name": "Jane", "family_name": "Doe", } ), ) def get_expected_to_str(self): return "jane@email.invalid" @override_settings( SOCIALACCOUNT_QUERY_EMAIL=False, ) def test_default_scopes_no_email(self): scopes = self.provider.get_default_scope() self.assertIn("aq:name", scopes) self.assertNotIn("email", scopes) @override_settings( SOCIALACCOUNT_QUERY_EMAIL=True, ) def test_default_scopes_email(self): scopes = self.provider.get_default_scope() self.assertIn("aq:name", scopes) self.assertIn("email", scopes) def test_scopes(self): request = RequestFactory().get(AuthentiqOAuth2Adapter.authorize_url) scopes = self.provider.get_scope_from_request(request) self.assertIn("openid", scopes) self.assertIn("aq:name", scopes) def test_dynamic_scopes(self): request = RequestFactory().get( AuthentiqOAuth2Adapter.authorize_url, dict(scope="foo") ) scopes = self.provider.get_scope_from_request(request) self.assertIn("openid", scopes) self.assertIn("aq:name", scopes) self.assertIn("foo", scopes) @override_settings( SOCIALACCOUNT_QUERY_EMAIL=True, SOCIALACCOUNT_EMAIL_REQUIRED=True, SOCIALACCOUNT_EMAIL_VERIFICATION=True, ) def test_scopes_required_verified_email(self): request = RequestFactory().get(AuthentiqOAuth2Adapter.authorize_url) scopes = self.provider.get_scope_from_request(request) self.assertIn("email~rs", scopes) self.assertNotIn("email", scopes) @override_settings( SOCIALACCOUNT_QUERY_EMAIL=True, SOCIALACCOUNT_EMAIL_REQUIRED=False, SOCIALACCOUNT_EMAIL_VERIFICATION=True, ) def test_scopes_optional_verified_email(self): request = RequestFactory().get(AuthentiqOAuth2Adapter.authorize_url) scopes = self.provider.get_scope_from_request(request) self.assertIn("email~s", scopes) self.assertNotIn("email", scopes) @override_settings( SOCIALACCOUNT_QUERY_EMAIL=True, SOCIALACCOUNT_EMAIL_REQUIRED=True, SOCIALACCOUNT_EMAIL_VERIFICATION=False, ) def test_scopes_required_email(self): request = RequestFactory().get(AuthentiqOAuth2Adapter.authorize_url) scopes = self.provider.get_scope_from_request(request) self.assertIn("email~r", scopes) self.assertNotIn("email", scopes) @override_settings( SOCIALACCOUNT_QUERY_EMAIL=True, SOCIALACCOUNT_EMAIL_REQUIRED=False, SOCIALACCOUNT_EMAIL_VERIFICATION=False, ) def test_scopes_optional_email(self): request = RequestFactory().get(AuthentiqOAuth2Adapter.authorize_url) scopes = self.provider.get_scope_from_request(request) self.assertIn("email", scopes) ================================================ FILE: tests/apps/socialaccount/providers/baidu/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/baidu/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.baidu.provider import BaiduProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class BaiduTests(OAuth2TestsMixin, TestCase): provider_id = BaiduProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ {"portrait": "78c0e9839de59bbde7859ccf43", "uname": "\u90dd\u56fd\u715c", "uid": "3225892368"}""", ) def get_expected_to_str(self): return "\u90dd\u56fd\u715c" ================================================ FILE: tests/apps/socialaccount/providers/base/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/base/test_provider.py ================================================ from unittest.mock import Mock import pytest from allauth.account.models import EmailAddress from allauth.socialaccount.providers.base.provider import Provider @pytest.mark.parametrize( "email,emails,expected_email,expected_emails", [ ( "a@A.COM", [EmailAddress(email="A@a.com")], "a@a.com", [EmailAddress(email="a@a.com")], ), ( None, [ EmailAddress(email="A@a.com", primary=True), EmailAddress(email="b-AT-b.com"), EmailAddress(email="c@c.com", primary=False), ], "a@a.com", [EmailAddress(email="a@a.com"), EmailAddress(email="c@c.com")], ), ( "a@A.COM", [EmailAddress(email="invalid.com")], "a@a.com", [EmailAddress(email="a@a.com")], ), ( "a@A.COM", [EmailAddress(email="is@valid.com")], "a@a.com", [ EmailAddress(email="a@a.com"), EmailAddress(email="is@valid.com"), ], ), ( "invalid@test.email", [ EmailAddress(email="invalid@test.email"), ], None, [], ), ( "invalid@test.email", [ EmailAddress(email="invalid@test.email"), EmailAddress(email="valid@test.email"), ], "valid@test.email", [EmailAddress(email="valid@test.email")], ), ], ) def test_cleanup_email_addresses(email, emails, expected_email, expected_emails): app = Mock() app.settings = {"verified_email": None} provider = Provider(None, app=app) provider.id = "test" email = provider.cleanup_email_addresses(email, emails) assert email == expected_email assert len(emails) == len(expected_emails) for i, addr in enumerate(emails): assert addr.email == expected_emails[i].email ================================================ FILE: tests/apps/socialaccount/providers/basecamp/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/basecamp/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.basecamp.provider import BasecampProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class BasecampTests(OAuth2TestsMixin, TestCase): provider_id = BasecampProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "expires_at": "2012-03-22T16:56:48-05:00", "identity": { "id": 9999999, "first_name": "Jason Fried", "last_name": "Jason Fried", "email_address": "jason@example.com" }, "accounts": [ { "product": "bcx", "id": 88888888, "name": "Wayne Enterprises, Ltd.", "href": "https://basecamp.com/88888888/api/v1" }, { "product": "bcx", "id": 77777777, "name": "Veidt, Inc", "href": "https://basecamp.com/77777777/api/v1" }, { "product": "campfire", "id": 44444444, "name": "Acme Shipping Co.", "href": "https://acme4444444.campfirenow.com" } ] }""", ) def get_expected_to_str(self): return "jason@example.com" ================================================ FILE: tests/apps/socialaccount/providers/battlenet/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/battlenet/tests.py ================================================ import json from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.battlenet.provider import BattleNetProvider from allauth.socialaccount.providers.battlenet.views import _check_errors from allauth.socialaccount.providers.oauth2.client import OAuth2Error from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class BattleNetTests(OAuth2TestsMixin, TestCase): provider_id = BattleNetProvider.id _uid = 123456789 _battletag = "LuckyDragon#1953" def get_mocked_response(self): data = {"battletag": self._battletag, "id": self._uid} return MockedResponse(HTTPStatus.OK, json.dumps(data)) def get_expected_to_str(self): return self._battletag def test_valid_response_no_battletag(self): data = {"id": 12345} response = MockedResponse(HTTPStatus.OK, json.dumps(data)) self.assertEqual(_check_errors(response), data) def test_invalid_data(self): response = MockedResponse(HTTPStatus.OK, json.dumps({})) with self.assertRaises(OAuth2Error): # No id, raises _check_errors(response) def test_profile_invalid_response(self): data = { "code": HTTPStatus.FORBIDDEN, "type": "Forbidden", "detail": "Account Inactive", } response = MockedResponse(HTTPStatus.UNAUTHORIZED, json.dumps(data)) with self.assertRaises(OAuth2Error): # no id, 4xx code, raises _check_errors(response) def test_error_response(self): body = json.dumps({"error": "invalid_token"}) response = MockedResponse(HTTPStatus.BAD_REQUEST, body) with self.assertRaises(OAuth2Error): # no id, 4xx code, raises _check_errors(response) def test_service_not_found(self): response = MockedResponse(596, "

    596 Service Not Found

    ") with self.assertRaises(OAuth2Error): # bad json, 5xx code, raises _check_errors(response) def test_invalid_response(self): response = MockedResponse(HTTPStatus.OK, "invalid json data") with self.assertRaises(OAuth2Error): # bad json, raises _check_errors(response) def test_extra_data(self): self.login(self.get_mocked_response()) account = SocialAccount.objects.get(uid=str(self._uid)) self.assertEqual(account.extra_data["battletag"], self._battletag) self.assertEqual(account.extra_data["id"], self._uid) self.assertEqual(account.extra_data["region"], "us") ================================================ FILE: tests/apps/socialaccount/providers/bitbucket_oauth2/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/bitbucket_oauth2/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from django.test.utils import override_settings from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.bitbucket_oauth2.provider import ( BitbucketOAuth2Provider, ) from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse @override_settings(SOCIALACCOUNT_QUERY_EMAIL=True, SOCIALACCOUNT_STORE_TOKENS=True) class BitbucketOAuth2Tests(OAuth2TestsMixin, TestCase): provider_id = BitbucketOAuth2Provider.id response_data = """ { "created_on": "2011-12-20T16:34:07.132459+00:00", "display_name": "tutorials account", "links": { "avatar": { "href": "https://bitbucket-assetroot.s3.amazonaws.com/c/photos/2013/Nov/25/tutorials-avatar-1563784409-6_avatar.png" }, "followers": { "href": "https://api.bitbucket.org/2.0/users/tutorials/followers" }, "following": { "href": "https://api.bitbucket.org/2.0/users/tutorials/following" }, "html": { "href": "https://bitbucket.org/tutorials" }, "repositories": { "href": "https://api.bitbucket.org/2.0/repositories/tutorials" }, "self": { "href": "https://api.bitbucket.org/2.0/users/tutorials" } }, "location": "Santa Monica, CA", "type": "user", "username": "tutorials", "uuid": "{c788b2da-b7a2-404c-9e26-d3f077557007}", "website": "https://tutorials.bitbucket.org/" } """ # noqa email_response_data = """ { "page": 1, "pagelen": 10, "size": 1, "values": [ { "email": "tutorials@bitbucket.org", "is_confirmed": true, "is_primary": true, "links": { "self": { "href": "https://api.bitbucket.org/2.0/user/emails/tutorials@bitbucket.org" } }, "type": "email" }, { "email": "tutorials+secondary@bitbucket.org", "is_confirmed": true, "is_primary": true, "links": { "self": { "href": "https://api.bitbucket.org/2.0/user/emails/tutorials+secondary@bitbucket.org" } }, "type": "email" } ] } """ # noqa def get_mocked_response(self): return [ MockedResponse(HTTPStatus.OK, self.response_data), MockedResponse(HTTPStatus.OK, self.email_response_data), MockedResponse(HTTPStatus.OK, self.response_data), MockedResponse(HTTPStatus.OK, self.email_response_data), ] def get_expected_to_str(self): return "tutorials" def test_provider_account(self): self.login(self.get_mocked_response()) socialaccount = SocialAccount.objects.get(uid="tutorials") self.assertEqual(socialaccount.user.username, "tutorials") self.assertEqual(socialaccount.user.email, "tutorials@bitbucket.org") account = socialaccount.get_provider_account() self.assertEqual(account.to_str(), "tutorials") self.assertEqual(account.get_profile_url(), "https://bitbucket.org/tutorials") self.assertEqual( account.get_avatar_url(), "https://bitbucket-assetroot.s3.amazonaws.com/c/photos/2013/Nov/25/tutorials-avatar-1563784409-6_avatar.png", # noqa ) ================================================ FILE: tests/apps/socialaccount/providers/bitly/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/bitly/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.bitly.provider import BitlyProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class BitlyTests(OAuth2TestsMixin, TestCase): provider_id = BitlyProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "data": { "apiKey": "R_f6397a37e765574f2e198dba5bb59522", "custom_short_domain": null, "display_name": null, "full_name": "Bitly API Oauth Demo Account", "is_enterprise": false, "login": "bitlyapioauthdemo", "member_since": 1331567982, "profile_image": "http://bitly.com/u/bitlyapioauthdemo.png", "profile_url": "http://bitly.com/u/bitlyapioauthdemo", "share_accounts": [], "tracking_domains": [] }, "status_code": 200, "status_txt": "OK" }""", ) def get_expected_to_str(self): return "bitlyapioauthdemo" ================================================ FILE: tests/apps/socialaccount/providers/box/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/box/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.box.provider import BoxOAuth2Provider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class BoxOAuth2Tests(OAuth2TestsMixin, TestCase): provider_id = BoxOAuth2Provider.id def get_mocked_response(self): return [ MockedResponse( HTTPStatus.OK, """{ "type": "user", "id": "1185237519", "name": "Balls Johnson", "login": "balls@example.com", "created_at": "2017-02-18T21:16:39-08:00", "modified_at": "2017-02-18T21:19:11-08:00", "language": "en", "timezone": "America/Los_Angeles", "space_amount": 10737418240, "space_used": 0, "max_upload_size": 2147483648, "status": "active", "job_title": "", "phone": "123-345-5555", "address": "", "avatar_url": "https://app.box.com/api/avatar/large/1185237519" }""", ) ] def get_expected_to_str(self): return "balls@example.com" ================================================ FILE: tests/apps/socialaccount/providers/cilogon/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/cilogon/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.cilogon.provider import CILogonProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class CILogonTests(OAuth2TestsMixin, TestCase): provider_id = CILogonProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "email": "johndoe@example.edu", "eppn": "u1234567@example.edu", "firstname": "John", "lastname": "Doe", "idp_name": "Example University", "sub": "http://cilogon.org/serverA/users/1234567" }""", ) def get_expected_to_str(self): return "johndoe@example.edu" ================================================ FILE: tests/apps/socialaccount/providers/clever/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/clever/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.clever.provider import CleverProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class CleverOAuth2Tests(OAuth2TestsMixin, TestCase): provider_id = CleverProvider.id def get_mocked_response(self): return [ MockedResponse( HTTPStatus.OK, """{ "type": "user", "data": { "id": "62027798269867124d10259e", "district": "6202763c8243d2100123dae5", "type": "user", "authorized_by": "district" }, "links": [ { "rel": "self", "uri": "/me" }, { "rel": "canonical", "uri": "/v3.0/users/62027798269867124d10259e" }, { "rel": "district", "uri": "/v3.0/districts/6202763c8243d2100123dae5" } ] }""", ), MockedResponse( HTTPStatus.OK, """{ "data": { "id": "62027798269867124d10259e", "roles": { "district_admin": {}, "contact": {} } } }""", ), ] def get_expected_to_str(self): return "Clever" ================================================ FILE: tests/apps/socialaccount/providers/coinbase/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/coinbase/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.coinbase.provider import CoinbaseProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class CoinbaseTests(OAuth2TestsMixin, TestCase): provider_id = CoinbaseProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "id": "9da7a204-544e-5fd1-9a12-61176c5d4cd8", "name": "User One", "username": "user1", "email": "user1@example.com", "profile_location": null, "profile_bio": null, "profile_url": "https://coinbase.com/user1", "avatar_url": "https://images.coinbase.com/avatar?h=vR%2FY8igBoPwuwGren5JMwvDNGpURAY%2F0nRIOgH%2FY2Qh%2BQ6nomR3qusA%2Bh6o2%0Af9rH&s=128", "resource": "user", "resource_path": "/v2/user" }""", ) def get_expected_to_str(self): return "user1" ================================================ FILE: tests/apps/socialaccount/providers/dataporten/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/dataporten/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.dataporten.provider import DataportenProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class DataportenTest(OAuth2TestsMixin, TestCase): provider_id = DataportenProvider.id def setUp(self): super().setUp() self.mock_data = { "userid": "76a7a061-3c55-430d-8ee0-6f82ec42501f", "userid_sec": ["feide:andreas@uninett.no"], "name": "Andreas \u00c5kre Solberg", "email": "andreas.solberg@uninett.no", "profilephoto": "p:a3019954-902f-45a3-b4ee-bca7b48ab507", "groups": [{}], } def get_login_response_json(self, with_refresh_token=True): rt = "" if with_refresh_token: rt = ',"refresh_token": "testrf"' return ( """{ "access_token":"testac", "expires_in":3600, "scope": "userid profile groups" %s }""" % rt ) def get_mocked_response(self): return MockedResponse( status_code=HTTPStatus.OK, content="""{ "user": { "userid": "76a7a061-3c55-430d-8ee0-6f82ec42501f", "userid_sec": ["feide:andreas@uninett.no"], "name": "Andreas \u00c5kre Solberg", "email": "andreas.solberg@uninett.no", "profilephoto": "p:a3019954-902f-45a3-b4ee-bca7b48ab507" }, "audience": "app123id" }""", headers={"content-type": "application/json"}, ) def get_expected_to_str(self): return "andreas.solberg@uninett.no" def test_extract_uid(self): uid = self.provider.extract_uid(self.mock_data) self.assertEqual(uid, self.mock_data["userid"]) def test_extract_extra_data(self): # All the processing is done in the complete_login view, and thus # the data should be returned unaltered extra_data = self.provider.extract_extra_data(self.mock_data) self.assertEqual(extra_data, self.mock_data) def test_extract_common_fields(self): # The main task of this function is to parse the data in order to # find the Feide username, and if not, use the email common_fields = self.provider.extract_common_fields(self.mock_data) self.assertEqual(common_fields["username"], "andreas") # Test correct behaviour when Feide username is unavailable new_mock_data = dict(self.mock_data) new_mock_data["userid_sec"] = [] new_common_fields = self.provider.extract_common_fields(new_mock_data) self.assertEqual(new_common_fields["username"], "andreas.solberg") ================================================ FILE: tests/apps/socialaccount/providers/daum/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/daum/tests.py ================================================ import json from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.daum.provider import DaumProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class DaumTests(OAuth2TestsMixin, TestCase): provider_id = DaumProvider.id def get_mocked_response(self): result = {} result["userid"] = "38DTh" result["id"] = 46287445 result["nickname"] = "xncbf" result["bigImagePath"] = "https://img1.daumcdn.net/thumb/" result["openProfile"] = "https://img1.daumcdn.net/thumb/" body = {} body["code"] = HTTPStatus.OK body["message"] = "OK" body["result"] = result return MockedResponse(HTTPStatus.OK, json.dumps(body)) def get_expected_to_str(self): return "xncbf" ================================================ FILE: tests/apps/socialaccount/providers/digitalocean/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/digitalocean/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.digitalocean.provider import DigitalOceanProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class DigitalOceanTests(OAuth2TestsMixin, TestCase): provider_id = DigitalOceanProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "account": { "droplet_limit": 25, "floating_ip_limit": 5, "email": "sammy@example.com", "uuid": "b6fr89dbf6d9156cace5f3c78dc9851d957381ef", "email_verified": true, "status": "active", "status_message": "" } } """, ) def get_login_response_json(self, with_refresh_token=True): return """ { "access_token": "testac", "token_type": "bearer", "expires_in": 2592000, "refresh_token": "00a3aae641658d", "scope": "read write", "info": { "name": "Sammy the Shark", "email":"sammy@example.com", "uuid":"b6fr89dbf6d9156cace5f3c78dc9851d957381ef" } }""" def get_expected_to_str(self): return "sammy@example.com" ================================================ FILE: tests/apps/socialaccount/providers/dingtalk/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/dingtalk/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.dingtalk.provider import DingTalkProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class DingTalkTests(OAuth2TestsMixin, TestCase): provider_id = DingTalkProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "nick": "aiden", "unionId": "hTaCSb1nM4RXii6jaQvHZqQiEiE", "avatarUrl": "https://static-legacy.dingtalk.com/media/lADPDg7mViaksW3NBJPNBJI_1170_1171.jpg", "openId": "ELdCPlk0V2LodZHx3n0p5AiEiE" }""", ) def get_login_response_json(self, with_refresh_token=True): return """{ "accessToken": "testac", "expireIn": "3600", "refreshToken": "testrf" }""" def get_expected_to_str(self): return "aiden" ================================================ FILE: tests/apps/socialaccount/providers/discogs/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/discogs/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.discogs.provider import DiscogsProvider from tests.apps.socialaccount.base import OAuthTestsMixin from tests.mocking import MockedResponse class DiscogsTests(OAuthTestsMixin, TestCase): provider_id = DiscogsProvider.id def get_mocked_response(self): return [ MockedResponse( HTTPStatus.OK, r'{"id": 5788917, "username": "veljkoj", ' r'"resource_url": "https://api.discogs.com/users/veljkoj", ' r'"consumer_name": "myapp"}', ) ] def get_expected_to_str(self): return "veljkoj" def test_login(self): super().test_login() account = SocialAccount.objects.get(uid="5788917") discogs_account = account.get_provider_account() self.assertEqual(discogs_account.get_username(), "veljkoj") self.assertEqual( discogs_account.get_profile_url(), r"https://api.discogs.com/users/veljkoj", ) self.assertEqual(discogs_account.to_str(), "veljkoj") ================================================ FILE: tests/apps/socialaccount/providers/discord/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/discord/tests.py ================================================ from http import HTTPStatus from django.contrib.auth import get_user_model from django.test import TestCase from allauth.account.models import EmailAddress from allauth.account.utils import user_email, user_username from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.discord.provider import DiscordProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class DiscordTests(OAuth2TestsMixin, TestCase): provider_id = DiscordProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "id": "80351110224678912", "username": "nelly", "discriminator": "0", "global_name": "Nelly", "avatar": "8342729096ea3675442027381ff50dfe", "verified": true, "email": "nelly@example.com" }""", ) def get_expected_to_str(self): return "Nelly" def test_display_name(self, multiple_login=False): email = "user@example.com" user = get_user_model()(is_active=True) user_email(user, email) user_username(user, "user") user.set_password("test") user.save() EmailAddress.objects.create(user=user, email=email, primary=True, verified=True) self.client.login(username=user.username, password="test") self.login(self.get_mocked_response(), process="connect") if multiple_login: self.login( self.get_mocked_response(), with_refresh_token=False, process="connect", ) # get account sa = SocialAccount.objects.filter(user=user, provider=self.provider.id).get() # The following lines don't actually test that much, but at least # we make sure that the code is hit. provider_account = sa.get_provider_account() self.assertEqual(provider_account.to_str(), "Nelly") class OldDiscordTests(DiscordTests, TestCase): provider_id = DiscordProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "id": "80351110224678912", "username": "Nelly", "discriminator": "1337", "avatar": "8342729096ea3675442027381ff50dfe", "verified": true, "email": "nelly@example.com" }""", ) def get_expected_to_str(self): return "Nelly#1337" def test_display_name(self, multiple_login=False): email = "user@example.com" user = get_user_model()(is_active=True) user_email(user, email) user_username(user, "user") user.set_password("test") user.save() EmailAddress.objects.create(user=user, email=email, primary=True, verified=True) self.client.login(username=user.username, password="test") self.login(self.get_mocked_response(), process="connect") if multiple_login: self.login( self.get_mocked_response(), with_refresh_token=False, process="connect", ) # get account sa = SocialAccount.objects.filter(user=user, provider=self.provider.id).get() # The following lines don't actually test that much, but at least # we make sure that the code is hit. provider_account = sa.get_provider_account() self.assertEqual(provider_account.to_str(), "Nelly#1337") ================================================ FILE: tests/apps/socialaccount/providers/disqus/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/disqus/tests.py ================================================ from http import HTTPStatus from django.contrib.auth.models import User from django.test import TestCase from django.test.utils import override_settings from allauth.account import app_settings as account_settings from allauth.account.models import EmailAddress from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.disqus.provider import DisqusProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse @override_settings( SOCIALACCOUNT_AUTO_SIGNUP=True, ACCOUNT_SIGNUP_FORM_CLASS=None, ACCOUNT_EMAIL_VERIFICATION=account_settings.EmailVerificationMethod.MANDATORY, ) class DisqusTests(OAuth2TestsMixin, TestCase): provider_id = DisqusProvider.id def get_mocked_response( self, name="Raymond Penners", email="raymond.penners@example.com" ): return MockedResponse( HTTPStatus.OK, """ {"response": {"name": "%s", "avatar": { "permalink": "https://lh5.googleusercontent.com/photo.jpg" }, "email": "%s", "profileUrl": "https://plus.google.com/108204268033311374519", "id": "108204268033311374519" }} """ % (name, email), ) def get_expected_to_str(self): return "raymond.penners@example.com" def test_account_connect(self): email = "user@example.com" user = User.objects.create(username="user", is_active=True, email=email) user.set_password("test") user.save() EmailAddress.objects.create(user=user, email=email, primary=True, verified=True) self.client.login(username=user.username, password="test") self.login(self.get_mocked_response(), process="connect") # Check if we connected... self.assertTrue( SocialAccount.objects.filter(user=user, provider=DisqusProvider.id).exists() ) # For now, we do not pick up any new email addresses on connect self.assertEqual(EmailAddress.objects.filter(user=user).count(), 1) self.assertEqual(EmailAddress.objects.filter(user=user, email=email).count(), 1) ================================================ FILE: tests/apps/socialaccount/providers/douban/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/douban/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.douban.provider import DoubanProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class DoubanTests(OAuth2TestsMixin, TestCase): provider_id = DoubanProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ {"name": "guoqiao", "created": "2009-02-18 01:07:52", "is_suicide": false, "alt": "http://www.douban.com/people/qguo/", "avatar": "http://img3.douban.com/icon/u3659811-3.jpg", "signature": "", "uid": "qguo", "is_banned": false, "desc": "\u4e0d\u662f\u5f88\u7231\u8bfb\u4e66", "type": "user", "id": "3659811", "large_avatar": "http://img3.douban.com/icon/up3659811-3.jpg"} """, ) def get_expected_to_str(self): return "guoqiao" ================================================ FILE: tests/apps/socialaccount/providers/doximity/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/doximity/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.doximity.provider import DoximityProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class DoximityTests(OAuth2TestsMixin, TestCase): provider_id = DoximityProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "id": 41993552342, "npi": 1952635229, "firstname": "John", "middlename": "Henry", "maiden_name": null, "lastname": "Smith", "full_name": "Ahmed S Belal, MD", "gender": "M", "city": "San Francisco", "state": "CA", "zip": "94107", "phone": "(650) 200-3901", "fax": "888-416-8572", "email": "abelalmd@example.com", "address_1": "500 3rd St.", "address_2": "Suite 510", "lat": 42.3663926, "lon": -71.051395, "additional_locations": [{ "address_1": "12 Main st", "address_2": null, "city": "Cambridge", "state": "MA", "phone": "555-555-5555", "fax": null, "zip": "02138" }], "credentials": "MD", "verified": true, "description": "Chief of Cardiology", "medical_school": "UCSF School of Medicine", "residencies": ["Stanford Medical Center", "Mt Sinai Hospital"], "specialty": "Cardiology", "specialty_details": { "abbr": "Cards", "code": "CA00", "credential_id": 4, "name": "Cardiology", "id": "CA00" }, "hospitals": [{ "name": "Mills-Peninsula Health Services", "aha_id": "6930315" }], "subspecialties": ["General Cardiology", "Cardiac Disease"], "profile_photo": "https://s3.amazonaws.com/doximity_prod_uploads\ /profile_photos/7969/normal/profile.png", "colleague_count": 142 } """, ) def get_expected_to_str(self): return "abelalmd@example.com" ================================================ FILE: tests/apps/socialaccount/providers/draugiem/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/draugiem/tests.py ================================================ import time from hashlib import md5 from unittest.mock import Mock, patch from django.contrib.auth.models import User from django.test import RequestFactory, TestCase from django.urls import reverse from django.utils.http import urlencode from allauth import app_settings from allauth.socialaccount.internal import statekit from allauth.socialaccount.models import SocialAccount, SocialApp, SocialToken from allauth.socialaccount.providers.draugiem import views from allauth.socialaccount.providers.draugiem.provider import DraugiemProvider class DraugiemTests(TestCase): def setUp(self): # workaround to create a session. see: # https://code.djangoproject.com/ticket/11475 User.objects.create_user( "anakin", "skywalker@deathstar.example.com", "s1thrul3s" ) self.client.login(username="anakin", password="s1thrul3s") app = SocialApp.objects.create( provider=DraugiemProvider.id, name=DraugiemProvider.id, client_id="app123id", key=DraugiemProvider.id, secret="dummy", ) request = RequestFactory().get("/") self.provider = app.get_provider(request) if app_settings.SITES_ENABLED: from django.contrib.sites.models import Site app.sites.add(Site.objects.get_current()) self.app = app def get_draugiem_login_response(self): """ Sample Draugiem.lv response """ return { "apikey": "12345", "uid": "42", "users": { "42": { "age": "266", "imgl": "http://cdn.memegenerator.net/instances/500x/23395689.jpg", "surname": "Skywalker", "url": "/user/42/", "imgi": "http://cdn.memegenerator.net/instances/500x/23395689.jpg", "nick": "Sky Guy", "created": "09.11.1812 11:26:15", "deleted": "false", "imgm": "http://cdn.memegenerator.net/instances/500x/23395689.jpg", "sex": "M", "type": "User_Default", "uid": "42", "place": "London", "emailHash": "3f198f21434gfd2f2b4rs05939shk93f3815bc6aa", "name": "Anakin", "adult": "1", "birthday": "1750-09-13", "img": "http://cdn.memegenerator.net/instances/500x/23395689.jpg", } }, } def get_socialaccount(self, response, token): """ Returns SocialLogin based on the data from the request """ request = Mock() login = self.provider.sociallogin_from_response(request, response) login.token = token return login def mock_socialaccount_state(self): """ SocialLogin depends on Session state - a tuple of request params and a random string """ session = self.client.session session[statekit.STATES_SESSION_KEY] = { "12345": ({"process": "login", "scope": "", "auth_params": ""}, time.time()) } session.save() def test_login_redirect(self): response = self.client.get(reverse(views.login)) redirect_url = reverse(views.callback) full_redirect_url = f"http://testserver{redirect_url}" secret = self.app.secret + full_redirect_url redirect_url_hash = md5(secret.encode("utf-8")).hexdigest() params = { "app": self.app.client_id, "hash": redirect_url_hash, "redirect": full_redirect_url, } self.assertRedirects( response, f"{views.AUTHORIZE_URL}?{urlencode(params)}", fetch_redirect_response=False, ) def test_callback_no_auth_status(self): response = self.client.get(reverse(views.callback)) self.assertTemplateUsed(response, "socialaccount/authentication_error.html") def test_callback_invalid_auth_status(self): response = self.client.get(reverse(views.callback), {"dr_auth_status": "fail"}) self.assertTemplateUsed(response, "socialaccount/authentication_error.html") def test_callback(self): with patch( "allauth.socialaccount.providers.draugiem.views.draugiem_complete_login" ) as draugiem_complete_login: self.mock_socialaccount_state() response_json = self.get_draugiem_login_response() token = SocialToken(app=self.app, token=response_json["apikey"]) login = self.get_socialaccount(response_json, token) draugiem_complete_login.return_value = login response = self.client.get( reverse(views.callback), {"dr_auth_status": "ok", "dr_auth_code": "42"}, ) self.assertRedirects( response, "/accounts/profile/", fetch_redirect_response=False ) socialaccount = SocialAccount.objects.filter( provider=DraugiemProvider.id ).last() pacc = socialaccount.get_provider_account() assert ( pacc.get_avatar_url() == "http://cdn.memegenerator.net/instances/500x/23395689.jpg" ) assert pacc.to_str() == "Anakin" ================================================ FILE: tests/apps/socialaccount/providers/drip/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/drip/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.drip.provider import DripProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class DripTests(OAuth2TestsMixin, TestCase): provider_id = DripProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "users":[{ "email": "john@acme.com", "name": "John Doe", "time_zone": "America/Los_Angeles" }] }""", ) def get_expected_to_str(self): return "john@acme.com" ================================================ FILE: tests/apps/socialaccount/providers/dropbox/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/dropbox/tests.py ================================================ import json from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.dropbox.provider import DropboxOAuth2Provider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class DropboxOAuth2Tests(OAuth2TestsMixin, TestCase): provider_id = DropboxOAuth2Provider.id def get_mocked_response(self): payload = { "account_id": "dbid:ASDFasd3ASdfasdFAsd1AS2ASDF1aS-DfAs", "account_type": {".tag": "basic"}, "country": "US", "disabled": False, "email": "allauth@example.com", "email_verified": True, "is_paired": True, "locale": "en", "name": { "abbreviated_name": "AA", "display_name": "All Auth", "familiar_name": "All", "given_name": "All", "surname": "Auth", }, "profile_photo_url": ( "https://dl-web.dropbox.com/account_photo" "/get/dbid%ASDFasd3ASdfasdFAsd1AS2ASDF1aS" "-DfAs?size=128x128" ), "referral_link": "https://db.tt/ASDfAsDf", } return MockedResponse(HTTPStatus.OK, json.dumps(payload)) def get_expected_to_str(self): return "allauth@example.com" ================================================ FILE: tests/apps/socialaccount/providers/dummy/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/dummy/tests.py ================================================ from http import HTTPStatus from django.conf import settings from django.contrib.auth import get_user_model from django.urls import reverse from allauth.socialaccount.models import SocialAccount def test_login(client, db): resp = client.post(reverse("dummy_login")) assert resp.status_code == HTTPStatus.FOUND assert resp["location"].startswith(f"{reverse('dummy_authenticate')}?state=") resp = client.post( resp["location"], {"id": "123", "email": "a@b.com", "email_verified": True}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == settings.LOGIN_REDIRECT_URL get_user_model().objects.filter(email="a@b.com").exists() socialaccount = SocialAccount.objects.get(uid="123") account = socialaccount.get_provider_account() assert account.to_str() == "a@b.com" ================================================ FILE: tests/apps/socialaccount/providers/dwolla/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/dwolla/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.dwolla.provider import DwollaProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class DwollaTests(OAuth2TestsMixin, TestCase): provider_id = DwollaProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "id": "123", "_links":{"account":{"href":"http://localhost"}}, "name":"John Doe" }""", ) def get_login_response_json(self, with_refresh_token=True): rt = "" if with_refresh_token: rt = ',"refresh_token": "testrf"' return ( """{ "uid":"weibo", "access_token":"testac", "_links":{"account":{"href":"http://localhost"}} %s }""" % rt ) def get_expected_to_str(self): return "John Doe" ================================================ FILE: tests/apps/socialaccount/providers/edmodo/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/edmodo/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.edmodo.provider import EdmodoProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class EdmodoTests(OAuth2TestsMixin, TestCase): provider_id = EdmodoProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "url": "https://api.edmodo.com/users/74721257", "id": 74721257, "type": "teacher", "username": "getacclaim-teacher1", "user_title": null, "first_name": "Edmodo Test", "last_name": "Teacher", "time_zone": "America/New_York", "utc_offset": -18000, "locale": "en", "gender": null, "start_level": null, "end_level": null, "about": null, "premium": false, "school": {"url": "https://api.edmodo.com/schools/559253", "id": 559253}, "verified_institution_member": true, "coppa_verified": false, "subjects": null, "avatars": { "small": "https://api.edmodo.com/users/74721257/avatar?type=small&u=670329ncqnf8fxv7tya24byn5", "large": "https://api.edmodo.com/users/74721257/avatar?type=large&u=670329ncqnf8fxv7tya24byn5" }, "email":"test@example.com", "sync_enabled": false } """, ) # noqa def get_expected_to_str(self): return "getacclaim-teacher1" ================================================ FILE: tests/apps/socialaccount/providers/edx/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/edx/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.edx.provider import EdxProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class EdxTests(OAuth2TestsMixin, TestCase): provider_id = EdxProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "username":"krzysztof", "bio":null, "requires_parental_consent":true, "language_proficiencies":[ ], "name":"Krzysztof Hoffmann", "country":null, "social_links":[ ], "is_active":true, "profile_image":{ "image_url_small":"http://draft.navoica.pl/static/images/profiles/default_30.png", "image_url_full":"http://draft.navoica.pl/static/images/profiles/default_500.png", "image_url_large":"http://draft.navoica.pl/static/images/profiles/default_120.png", "image_url_medium":"http://draft.navoica.pl/static/images/profiles/default_50.png", "has_image":false }, "extended_profile":[ ], "year_of_birth":null, "level_of_education":null, "goals":"", "accomplishments_shared":false, "gender":null, "date_joined":"2019-09-21T07:48:31Z", "mailing_address":"", "email":"krzysztof.hoffmann@opi.org.pl", "account_privacy":"private" }""", ) def get_expected_to_str(self): return "krzysztof" ================================================ FILE: tests/apps/socialaccount/providers/eventbrite/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/eventbrite/tests.py ================================================ """Test Eventbrite OAuth2 v3 Flow.""" from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.eventbrite.provider import EventbriteProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class EventbriteTests(OAuth2TestsMixin, TestCase): """Test Class for Eventbrite OAuth2 v3.""" provider_id = EventbriteProvider.id def get_mocked_response(self): """Test authentication with an non-null image_id""" return MockedResponse( HTTPStatus.OK, """{ "emails": [{ "email": "test@example.com", "verified": true }], "id": "999999999", "name": "Andrew Godwin", "first_name": "Andrew", "last_name": "Godwin", "image_id": "99999999" }""", ) def get_expected_to_str(self): return "test@example.com" ================================================ FILE: tests/apps/socialaccount/providers/eveonline/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/eveonline/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.eveonline.provider import EveOnlineProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class EveOnlineTests(OAuth2TestsMixin, TestCase): provider_id = EveOnlineProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "CharacterID": 273042051, "CharacterName": "CCP illurkall", "ExpiresOn": "2014-05-23T15:01:15.182864Z", "Scopes": " ", "TokenType": "Character", "CharacterOwnerHash": "XM4D...FoY=" }""", ) def get_expected_to_str(self): return "CCP illurkall" ================================================ FILE: tests/apps/socialaccount/providers/evernote/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/evernote/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.evernote.provider import EvernoteProvider from tests.apps.socialaccount.base import OAuthTestsMixin from tests.mocking import MockedResponse class EvernoteTests(OAuthTestsMixin, TestCase): provider_id = EvernoteProvider.id def get_mocked_response(self): return [] def get_expected_to_str(self): return "Evernote" def get_access_token_response(self): return MockedResponse( HTTPStatus.OK, "oauth_token=S%3Ds1%3AU%3D9876%3AE%3D999999b0c50%3AC%3D14c1f89dd18%3AP%3D81%3AA%3Dpennersr%3AV%3D2%3AH%3Ddeadf00dd2d6aba7b519923987b4bf77&oauth_token_secret=&edam_shard=s1&edam_userId=591969&edam_expires=1457994271824&edam_noteStoreUrl=https%3A%2F%2Fsandbox.evernote.com%2Fshard%2Fs1%2Fnotestore&edam_webApiUrlPrefix=https%3A%2F%2Fsandbox.evernote.com%2Fshard%2Fs1%2F", # noqa {"content-type": "text/plain"}, ) ================================================ FILE: tests/apps/socialaccount/providers/exist/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/exist/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.exist.provider import ExistProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class ExistTests(OAuth2TestsMixin, TestCase): provider_id = ExistProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "id": 1, "username": "josh", "first_name": "Josh", "last_name": "Sharp", "bio": "I made this thing you're using.", "url": "http://hellocode.co/", "avatar": "https://exist.io/static/media/avatars/josh_2.png", "timezone": "Australia/Melbourne", "local_time": "2020-07-31T22:33:49.359+10:00", "private": false, "imperial_units": false, "imperial_distance": false, "imperial_weight": false, "imperial_energy": false, "imperial_liquid": false, "imperial_temperature": false, "attributes": [] } """, ) def get_expected_to_str(self): return "josh" ================================================ FILE: tests/apps/socialaccount/providers/facebook/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/facebook/tests.py ================================================ from http import HTTPStatus from django.contrib.auth import get_user_model from django.test import TestCase from django.test.client import RequestFactory from django.test.utils import override_settings from django.urls import reverse from allauth.account import app_settings as account_settings from allauth.account.models import EmailAddress from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.facebook.provider import FacebookProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse, mocked_response @override_settings( SOCIALACCOUNT_AUTO_SIGNUP=True, ACCOUNT_SIGNUP_FORM_CLASS=None, LOGIN_REDIRECT_URL="/accounts/profile/", ACCOUNT_EMAIL_VERIFICATION=account_settings.EmailVerificationMethod.NONE, SOCIALACCOUNT_PROVIDERS={"facebook": {"AUTH_PARAMS": {}, "VERIFIED_EMAIL": False}}, ) class FacebookTests(OAuth2TestsMixin, TestCase): provider_id = FacebookProvider.id facebook_data = """ { "id": "630595557", "name": "Raymond Penners", "first_name": "Raymond", "last_name": "Penners", "email": "raymond.penners@example.com", "link": "https://www.facebook.com/raymond.penners", "username": "raymond.penners", "birthday": "07/17/1973", "work": [ { "employer": { "id": "204953799537777", "name": "IntenCT" } } ], "timezone": 1, "locale": "nl_NL", "verified": true, "updated_time": "2012-11-30T20:40:33+0000" }""" def get_mocked_response(self, data=None): if data is None: data = self.facebook_data return MockedResponse(HTTPStatus.OK, data) def get_expected_to_str(self): return "raymond.penners" def test_username_conflict(self): User = get_user_model() User.objects.create(username="raymond.penners") self.login(self.get_mocked_response()) socialaccount = SocialAccount.objects.get(uid="630595557") self.assertEqual(socialaccount.user.username, "raymond") def test_username_based_on_provider(self): self.login(self.get_mocked_response()) socialaccount = SocialAccount.objects.get(uid="630595557") self.assertEqual(socialaccount.user.username, "raymond.penners") def test_username_based_on_provider_with_simple_name(self): data = '{"id": "1234567", "name": "Harvey McGillicuddy"}' self.login(self.get_mocked_response(data=data)) socialaccount = SocialAccount.objects.get(uid="1234567") self.assertEqual(socialaccount.user.username, "harvey") @override_settings( SOCIALACCOUNT_PROVIDERS={ "facebook": { "METHOD": "js_sdk", } }, ) def test_media_js(self): request = RequestFactory().get(reverse("account_login")) request.session = {} script = self.provider.media_js(request) self.assertTrue('"appId": "app123id"' in script) def test_token_auth(self): with mocked_response( {"access_token": "app_token"}, { "data": { "app_id": "app123id", "is_valid": True, } }, self.get_mocked_response(), ): login = self.provider.verify_token(None, {"access_token": "dummy"}) assert login.user.email == "raymond.penners@example.com" assert login.token.token == "dummy" def test_login_by_token(self): resp = self.client.get(reverse("account_login")) with mocked_response( {"access_token": "app_token"}, { "data": { "app_id": "app123id", "is_valid": True, } }, self.get_mocked_response(), ): resp = self.client.post( reverse("facebook_login_by_token"), data={"access_token": "dummy"}, ) self.assertRedirects( resp, "/accounts/profile/", fetch_redirect_response=False ) @override_settings( SOCIALACCOUNT_PROVIDERS={ "facebook": { "METHOD": "js_sdk", "AUTH_PARAMS": {"auth_type": "reauthenticate"}, "VERIFIED_EMAIL": False, } } ) def test_login_by_token_reauthenticate(self): resp = self.client.get(reverse("account_login")) nonce = resp.context["fb_data"]["loginOptions"]["auth_nonce"] with mocked_response( {"access_token": "app_token"}, { "data": { "app_id": "app123id", "is_valid": True, } }, {"auth_nonce": nonce}, self.get_mocked_response(), ): resp = self.client.post( reverse("facebook_login_by_token"), data={"access_token": "dummy"}, ) self.assertRedirects( resp, "/accounts/profile/", fetch_redirect_response=False ) @override_settings(SOCIALACCOUNT_PROVIDERS={"facebook": {"VERIFIED_EMAIL": True}}) def test_login_verified(self): emailaddress = self._login_verified() self.assertTrue(emailaddress.verified) def test_login_unverified(self): emailaddress = self._login_verified() self.assertFalse(emailaddress.verified) def _login_verified(self): self.login(self.get_mocked_response()) return EmailAddress.objects.get(email="raymond.penners@example.com") def test_limited_token(rf, db, settings, jwt_decode_bypass): settings.SOCIALACCOUNT_PROVIDERS = { "facebook": { "AUTH_PARAMS": {}, "VERIFIED_EMAIL": False, "APPS": [{"client_id": "123"}], } } request = rf.get("/") adapter = get_adapter(request) provider = adapter.get_provider(request, FacebookProvider.id) token = {"id_token": "X"} with jwt_decode_bypass( { "sub": "f123", "email": "e@mail.org", "given_name": "John", "family_name": "Doe", } ): login = provider.verify_token(request, token) assert login.account.uid == "f123" assert login.email_addresses[0].email == "e@mail.org" ================================================ FILE: tests/apps/socialaccount/providers/feedly/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/feedly/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.feedly.provider import FeedlyProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class FeedlyTests(OAuth2TestsMixin, TestCase): provider_id = FeedlyProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "id": "c805fcbf-3acf-4302-a97e-d82f9d7c897f", "email": "jim.smith@example.com", "givenName": "Jim", "familyName": "Smith", "picture": "https://www.google.com/profile_images/1771656873/bigger.jpg", "gender": "male", "locale": "en", "reader": "9080770707070700", "google": "115562565652656565656", "twitter": "jimsmith", "facebook": "", "wave": "2013.7" }""", ) def get_expected_to_str(self): return "jim.smith@example.com" ================================================ FILE: tests/apps/socialaccount/providers/feishu/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/feishu/tests.py ================================================ from django.test import TestCase from allauth.socialaccount.providers.feishu.provider import FeishuProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class FeishuTests(OAuth2TestsMixin, TestCase): provider_id = FeishuProvider.id def get_mocked_response(self): return [ MockedResponse( 0, """ {"data": {"access_token": "testac"}} """, ), MockedResponse( 0, """ { "code": 0, "data": { "access_token": "u-6U1SbDiM6XIH2DcTCPyeub", "avatar_url": "www.feishu.cn/avatar/icon", "avatar_thumb": "www.feishu.cn/avatar/icon_thumb", "avatar_middle": "www.feishu.cn/avatar/icon_middle", "avatar_big": "www.feishu.cn/avatar/icon_big", "expires_in": 7140, "name": "zhangsan", "en_name": "Three Zhang", "open_id": "ou-caecc734c2e3328a62489fe0648c4b98779515d3", "tenant_key": "736588c92lxf175d", "refresh_expires_in": 2591940, "refresh_token": "ur-t9HHgRCjMqGqIU9v05Zhos", "token_type": "Bearer" } } """, ), ] def get_expected_to_str(self): return "zhangsan" def get_login_response_json(self, with_refresh_token=True): return """{"app_access_token":"testac"}""" ================================================ FILE: tests/apps/socialaccount/providers/figma/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/figma/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.figma.provider import FigmaProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class FigmaTests(OAuth2TestsMixin, TestCase): provider_id = FigmaProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "id": "2600", "email": "johndoe@example.com", "handle": "John Doe", "img_url": "https://www.example.com/image.png" } """, ) def get_expected_to_str(self): return "John Doe" ================================================ FILE: tests/apps/socialaccount/providers/fivehundredpx/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/fivehundredpx/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.fivehundredpx.provider import FiveHundredPxProvider from tests.apps.socialaccount.base import OAuthTestsMixin from tests.mocking import MockedResponse class FiveHundredPxTests(OAuthTestsMixin, TestCase): provider_id = FiveHundredPxProvider.id def get_mocked_response(self): return [ MockedResponse( HTTPStatus.OK, """{ "user": { "id": 5751454, "username": "testuser", "firstname": "Test", "lastname": "User", "birthday": null, "sex": 0, "city": "San Francisco", "state": "California", "country": "United States", "registration_date": "2015-12-12T03:20:31-05:00", "about": "About me.", "usertype": 0, "fotomoto_on": true, "locale": "en", "show_nude": false, "allow_sale_requests": 1, "fullname": "Test User", "userpic_url": "https://pacdn.500px.org/10599609/8e20991262c468a866918dcbe2f7e9a30e2c2c9c/1.jpg?1", "userpic_https_url": "https://pacdn.500px.org/10599609/8e20991262c468a866918dcbe2f7e9a30e2c2c9c/1.jpg?1", "cover_url": "https://pacdn.500px.org/10599609/8e20991262c468a866918dcbe2f7e9a30e2c2c9c/cover_2048.jpg?7", "upgrade_status": 2, "store_on": true, "photos_count": 68, "galleries_count": 2, "affection": 1888, "in_favorites_count": 340, "friends_count": 181, "followers_count": 150, "analytics_code": null, "invite_pending": false, "invite_accepted": false, "email": "test@example.com", "shadow_email": "test@example.com", "upload_limit": null, "upload_limit_expiry": "2016-12-01T13:33:55-05:00", "upgrade_type": 2, "upgrade_status_expiry": "2017-05-27", "auth": { "facebook": 0, "twitter": 0, "google_oauth2": 0 }, "presubmit_for_licensing": null, "avatars": { "default": { "http": "https://pacdn.500px.org/10599609/8e20991262c468a866918dcbe2f7e9a30e2c2c9c/1.jpg?1", "https": "https://pacdn.500px.org/10599609/8e20991262c468a866918dcbe2f7e9a30e2c2c9c/1.jpg?1" }, "large": { "http": "https://pacdn.500px.org/10599609/8e20991262c468a866918dcbe2f7e9a30e2c2c9c/2.jpg?1", "https": "https://pacdn.500px.org/10599609/8e20991262c468a866918dcbe2f7e9a30e2c2c9c/2.jpg?1" }, "small": { "http": "https://pacdn.500px.org/10599609/8e20991262c468a866918dcbe2f7e9a30e2c2c9c/1.jpg?1", "https": "https://pacdn.500px.org/10599609/8e20991262c468a866918dcbe2f7e9a30e2c2c9c/1.jpg?1" }, "tiny": { "http": "https://pacdn.500px.org/10599609/8e20991262c468a866918dcbe2f7e9a30e2c2c9c/4.jpg?1", "https": "https://pacdn.500px.org/10599609/8e20991262c468a866918dcbe2f7e9a30e2c2c9c/4.jpg?1" } } } }""", ) ] # noqa def get_expected_to_str(self): return "testuser" ================================================ FILE: tests/apps/socialaccount/providers/flickr/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/flickr/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.flickr.provider import FlickrProvider from tests.apps.socialaccount.base import OAuthTestsMixin from tests.mocking import MockedResponse class FlickrTests(OAuthTestsMixin, TestCase): provider_id = FlickrProvider.id def get_mocked_response(self): # return [ MockedResponse( HTTPStatus.OK, r""" {"stat": "ok", "user": { "username": { "_content": "pennersr"}, "id": "12345678@N00"}} """, ), # noqa MockedResponse( HTTPStatus.OK, r""" {"person": {"username": {"_content": "pennersr"}, "photosurl": {"_content": "http://www.flickr.com/photos/12345678@N00/"}, "nsid": "12345678@N00", "path_alias": null, "photos": {"count": {"_content": 0}, "firstdatetaken": {"_content": null}, "views": {"_content": "28"}, "firstdate": {"_content": null}}, "iconserver": "0", "description": {"_content": ""}, "mobileurl": {"_content": "http://m.flickr.com/photostream.gne?id=6294613"}, "profileurl": { "_content": "http://www.flickr.com/people/12345678@N00/"}, "mbox_sha1sum": {"_content": "5e5b359c123e54f95236209c8808d607a5cdd21e"}, "ispro": 0, "location": {"_content": ""}, "id": "12345678@N00", "realname": {"_content": "raymond penners"}, "iconfarm": 0}, "stat": "ok"} """, ), ] # noqa def get_expected_to_str(self): return "pennersr" def test_login(self): super().test_login() account = SocialAccount.objects.get(uid="12345678@N00") f_account = account.get_provider_account() self.assertEqual(account.user.first_name, "raymond") self.assertEqual(account.user.last_name, "penners") self.assertEqual( f_account.get_profile_url(), "http://www.flickr.com/people/12345678@N00/", ) self.assertEqual(f_account.to_str(), "pennersr") class FlickrWithoutRealNameTests(OAuthTestsMixin, TestCase): """Separate test for Flickr accounts without real names""" provider_id = FlickrProvider.id def get_mocked_response(self): # return [ MockedResponse( HTTPStatus.OK, r""" {"stat": "ok", "user": { "username": { "_content": "pennersr"}, "id": "12345678@N00"}} """, ), # noqa MockedResponse( HTTPStatus.OK, r""" {"person": {"username": {"_content": "pennersr"}, "photosurl": {"_content": "http://www.flickr.com/photos/12345678@N00/"}, "nsid": "12345678@N00", "path_alias": null, "photos": {"count": {"_content": 0}, "firstdatetaken": {"_content": null}, "views": {"_content": "28"}, "firstdate": {"_content": null}}, "iconserver": "0", "description": {"_content": ""}, "mobileurl": {"_content": "http://m.flickr.com/photostream.gne?id=6294613"}, "profileurl": { "_content": "http://www.flickr.com/people/12345678@N00/"}, "mbox_sha1sum": {"_content": "5e5b359c123e54f95236209c8808d607a5cdd21e"}, "ispro": 0, "location": {"_content": ""}, "id": "12345678@N00", "realname": {"_content": ""}, "iconfarm": 0}, "stat": "ok"} """, ), ] # noqa def get_expected_to_str(self): return "pennersr" def test_login(self): super().test_login() account = SocialAccount.objects.get(uid="12345678@N00") f_account = account.get_provider_account() self.assertEqual(account.user.first_name, "") self.assertEqual(account.user.last_name, "") self.assertEqual( f_account.get_profile_url(), "http://www.flickr.com/people/12345678@N00/", ) self.assertEqual(f_account.to_str(), "pennersr") ================================================ FILE: tests/apps/socialaccount/providers/foursquare/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/foursquare/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.foursquare.provider import FoursquareProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class FoursquareTests(OAuth2TestsMixin, TestCase): provider_id = FoursquareProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ {"notifications": [{"item": {"unreadCount": 0}, "type": "notificationTray"}], "meta": {"code": 200}, "response": { "user": { "photo": { "prefix": "https://irs0.4sqi.net/img/user/", "suffix": "/blank_boy.png"}, "pings": false, "homeCity": "Athens, ESYE31", "id": "76077726", "badges": {"count": 0, "items": []}, "referralId": "u-76077726", "friends": { "count": 0, "groups": [{"count": 0, "items": [], "type": "friends", "name": "Mutual friends"}, {"count": 0, "items": [], "type": "others", "name": "Other friends"}] }, "createdAt": 1389624445, "tips": {"count": 0}, "type": "user", "bio": "", "relationship": "self", "lists": { "count": 1, "groups": [{"count": 1, "items": [{"description": "", "collaborative": false, "url": "/user/76077726/list/todos", "editable": false, "listItems": {"count": 0}, "id": "76077726/todos", "followers": {"count": 0}, "user": {"gender": "male", "firstName": "\u03a1\u03c9\u03bc\u03b1\u03bd\u03cc\u03c2", "relationship": "self", "photo": {"prefix": "https://irs0.4sqi.net/img/user/", "suffix": "/blank_boy.png"}, "lastName": "\u03a4\u03c3\u03bf\u03c5\u03c1\u03bf\u03c0\u03bb\u03ae\u03c2", "id": "76077726"}, "public": false, "canonicalUrl": "https://foursquare.com/user/76077726/list/todos", "name": "My to-do list"}], "type": "created"}, {"count": 0, "items": [], "type": "followed"}] }, "photos": {"count": 0, "items": []}, "checkinPings": "off", "scores": {"max": 0, "checkinsCount": 0, "goal": 50, "recent": 0}, "checkins": {"count": 0, "items": []}, "firstName": "\u03a1\u03c9\u03bc\u03b1\u03bd\u03cc\u03c2", "gender": "male", "contact": {"email": "romdimtsouroplis@example.com"}, "lastName": "\u03a4\u03c3\u03bf\u03c5\u03c1\u03bf\u03c0\u03bb\u03ae\u03c2", "following": {"count": 0, "groups": [{"count": 0, "items": [], "type": "following", "name": "Mutual following"}, {"count": 0, "items": [], "type": "others", "name": "Other following"}]}, "requests": {"count": 0}, "mayorships": {"count": 0, "items": []}} } } """, ) def get_expected_to_str(self): return "romdimtsouroplis@example.com" ================================================ FILE: tests/apps/socialaccount/providers/frontier/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/frontier/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.frontier.provider import FrontierProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class FrontierTests(OAuth2TestsMixin, TestCase): provider_id = FrontierProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "email": "johndoe@example.com", "customer_id": "1234567", "firstname": "John", "developer": false, "lastname": "Doe", "allowedDownloads": ["FORC-FDEV-D-1010", "FORC-FDEV-D-1012", "COMBAT_TUTORIAL_DEMO", "FORC-FDEV-D-1013", "PUBLIC_TEST_SERVER", "FORC_FDEV_V_ADDER_LRPO", "FORC_FDEV_V_CHALLENGER_LRPO", "FORC_FDEV_V_CHIEFTAIN_LRPO", "FORC_FDEV_V_CRUSADER_LRPO", "FORC_FDEV_V_ANACONDA_LRPO", "FORC_FDEV_V_ASP_LRPO", "FORC_FDEV_V_ASP_SCOUT_LRPO", "FORC_FDEV_V_BELUGA_LRPO", "FORC_FDEV_V_COBRA_MKIII_LRPO", "FORC_FDEV_V_DIAMOND_EXPLORER_LRPO", "FORC_FDEV_V_COBRA_MKIV_LRPO", "FORC_FDEV_V_DIAMOND_SCOUT_LRPO", "FORC_FDEV_V_DOLPHIN_LRPO", "FORC_FDEV_V_EAGLE_LRPO", "FORC_FDEV_V_FEDERAL_ASSAULT_LRPO", "FORC_FDEV_V_FEDERAL_CORVETTE_LRPO", "FORC_FDEV_V_FEDDROP_LRPO", "FORC_FDEV_V_FEDERAL_FIGHTER_LRPO", "FORC_FDEV_V_FEDERAL_GUNSHIP_LRPO", "FORC_FDEV_V_FERDELANCE_LRPO", "FORC_FDEV_V_HAULER_LRPO", "FORC_FDEV_V_CLIPPER_LRPO", "FORC_FDEV_V_IMPERIAL_COURIER_LRPO", "FORC_FDEV_V_IMPERIAL_CUTTER_LRPO", "FORC_FDEV_V_IMPERIAL_EAGLE_LRPO", "FORC_FDEV_V_IMPERIAL_FIGHTER_LRPO", "FORC_FDEV_V_KEELBACK_LRPO", "FORC_FDEV_V_KRAIT_LRPO", "FORC_FDEV_V_KRAIT_LITE_LRPO", "FORC_FDEV_V_MAMBA_LRPO", "FORC_FDEV_V_ORCA_LRPO", "FORC_FDEV_V_PYTHON_LRPO", "FORC_FDEV_V_SIDEWINDER_LRPO", "FORC_FDEV_V_TAIPAN_LRPO", "FORC_FDEV_V_MAMMOTH_LRPO", "FORC_FDEV_V_TYPE6_LRPO", "FORC_FDEV_V_TYPE7_LRPO", "FORC_FDEV_V_TYPE9_LRPO", "FORC_FDEV_V_VIPER_MKIII_LRPO", "FORC_FDEV_V_VIPER_MKIV_LRPO", "FORC_FDEV_V_VULTURE_LRPO", "FORC-FDEV-D-1022", "FORC_FDEV_V_DECAL_1091", "FORC_FDEV_V_DECAL_1100", "FORC_FDEV_V_DECAL_1149", "FORC_FDEV_V_DECAL_1150", "FORC_FDEV_V_DECAL_1151", "FORC_FDEV_V_DECAL_1176", "FORC_FDEV_V_DECAL_1177", "FORC-FDEV-DO-1000", "FORC-FDEV-DO-1003", "FORC-FDEV-DO-1006", "PUBLIC_TEST_SERVER_OD"], "platform": "frontier" }""", ) def get_expected_to_str(self): return "johndoe@example.com" ================================================ FILE: tests/apps/socialaccount/providers/fxa/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/fxa/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.fxa.provider import FirefoxAccountsProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class FirefoxAccountsTests(OAuth2TestsMixin, TestCase): provider_id = FirefoxAccountsProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "uid":"6d940dd41e636cc156074109b8092f96", "email":"user@example.com" }""", ) def get_expected_to_str(self): return "user@example.com" ================================================ FILE: tests/apps/socialaccount/providers/gitea/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/gitea/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.gitea.provider import GiteaProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class GiteaTests(OAuth2TestsMixin, TestCase): provider_id = GiteaProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "id": 4940, "login": "giteauser", "full_name": "", "email": "giteauser@example.com", "avatar_url": "https://gitea.com/user/avatar/giteauser/-1", "language": "en-US", "is_admin": true, "last_login": "2021-08-20T20:07:39Z", "created": "2018-05-03T16:04:34Z", "restricted": false, "active": true, "prohibit_login": false, "location": "", "website": "", "description": "", "visibility": "public", "followers_count": 0, "following_count": 0, "starred_repos_count": 0, "username": "giteauser" }""", ) def get_expected_to_str(self): return "giteauser" def test_account_name_null(self): """String conversion when Gitea responds with empty username""" data = """{ "id": 4940, "login": "giteauser", "username": null }""" self.login(MockedResponse(HTTPStatus.OK, data)) socialaccount = SocialAccount.objects.get(uid="4940") self.assertIsNone(socialaccount.extra_data.get("name")) account = socialaccount.get_provider_account() self.assertIsNotNone(account.to_str()) self.assertEqual(account.to_str(), "giteauser") ================================================ FILE: tests/apps/socialaccount/providers/github/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/github/tests.py ================================================ from http import HTTPStatus from unittest.mock import patch from django.test import TestCase from allauth.account.models import EmailAddress from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.github.provider import GitHubProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class GitHubTests(OAuth2TestsMixin, TestCase): provider_id = GitHubProvider.id def get_mocked_response(self): return [ MockedResponse( HTTPStatus.OK, """ { "type":"User", "organizations_url":"https://api.github.com/users/pennersr/orgs", "gists_url":"https://api.github.com/users/pennersr/gists{/gist_id}", "received_events_url":"https://api.github.com/users/pennersr/received_events", "gravatar_id":"8639768262b8484f6a3380f8db2efa5b", "followers":16, "blog":"http://www.intenct.info", "avatar_url":"https://secure.gravatar.com/avatar/8639768262b8484f6a3380f8db2efa5b?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png", "login":"pennersr", "created_at":"2010-02-10T12:50:51Z", "company":"IntenCT", "subscriptions_url":"https://api.github.com/users/pennersr/subscriptions", "public_repos":14, "hireable":false, "url":"https://api.github.com/users/pennersr", "public_gists":0, "starred_url":"https://api.github.com/users/pennersr/starred{/owner}{/repo}", "html_url":"https://github.com/pennersr", "location":"The Netherlands", "bio":null, "name":"Raymond Penners", "repos_url":"https://api.github.com/users/pennersr/repos", "followers_url":"https://api.github.com/users/pennersr/followers", "id":201022, "following":0, "email":"raymond.penners@intenct.nl", "events_url":"https://api.github.com/users/pennersr/events{/privacy}", "following_url":"https://api.github.com/users/pennersr/following" }""", ), MockedResponse( HTTPStatus.OK, """ [{ "email": "octocat@github.com", "verified": true, "primary": true, "visibility": "public" }] """, ), ] def get_expected_to_str(self): return "pennersr" def test_account_name_null(self): """String conversion when GitHub responds with empty name""" mocks = [ MockedResponse( HTTPStatus.OK, """ { "type": "User", "id": 201022, "login": "pennersr", "name": null } """, ), MockedResponse( HTTPStatus.OK, """ [ { "email": "octocat@github.com", "verified": true, "primary": true, "visibility": "public" }, { "email": "secONDary@GitHub.COM", "verified": true, "primary": false, "visibility": "public" } ] """, ), ] with patch( "allauth.socialaccount.adapter.DefaultSocialAccountAdapter.populate_user" ) as populate_mock: self.login(mocks) populate_data = populate_mock.call_args[0][2] assert populate_data["email"] == "octocat@github.com" socialaccount = SocialAccount.objects.get(uid="201022") self.assertIsNone(socialaccount.extra_data.get("name")) account = socialaccount.get_provider_account() self.assertIsNotNone(account.to_str()) self.assertEqual(account.to_str(), "pennersr") self.assertEqual(socialaccount.user.email, "octocat@github.com") self.assertTrue( EmailAddress.objects.filter( primary=False, verified=True, email="secondary@github.com", user=socialaccount.user, ).exists() ) self.assertTrue("emails" not in socialaccount.extra_data) ================================================ FILE: tests/apps/socialaccount/providers/gitlab/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/gitlab/tests.py ================================================ import json from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.gitlab.provider import GitLabProvider from allauth.socialaccount.providers.gitlab.views import _check_errors from allauth.socialaccount.providers.oauth2.client import OAuth2Error from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class GitLabTests(OAuth2TestsMixin, TestCase): provider_id = GitLabProvider.id _uid = 2 def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "avatar_url": "https://secure.gravatar.com/avatar/123", "bio": null, "can_create_group": true, "can_create_project": true, "color_scheme_id": 5, "confirmed_at": "2015-03-02T16:53:58.370Z", "created_at": "2015-03-02T16:53:58.885Z", "current_sign_in_at": "2018-06-12T18:44:49.985Z", "email": "mr.bob@gitlab.example.com", "external": false, "id": 2, "identities": [], "last_activity_on": "2018-06-11", "last_sign_in_at": "2018-05-31T14:59:44.527Z", "linkedin": "", "location": null, "name": "Mr Bob", "organization": null, "projects_limit": 10, "shared_runners_minutes_limit": 2000, "skype": "", "state": "active", "theme_id": 6, "twitter": "mrbob", "two_factor_enabled": true, "username": "mr.bob", "web_url": "https://gitlab.example.com/u/mr.bob", "website_url": "" } """, ) def get_expected_to_str(self): return "mr.bob" def test_valid_response(self): data = {"id": 12345} response = MockedResponse(HTTPStatus.OK, json.dumps(data)) self.assertEqual(_check_errors(response), data) def test_invalid_data(self): response = MockedResponse(HTTPStatus.OK, json.dumps({})) with self.assertRaises(OAuth2Error): # No id, raises _check_errors(response) def test_account_invalid_response(self): body = ( "403 Forbidden - You (@domain.com) must accept the Terms of " "Service in order to perform this action. Please access GitLab " "from a web browser to accept these terms." ) response = MockedResponse(HTTPStatus.FORBIDDEN, body) # GitLab allow users to login with their API and provides # an error requiring the user to accept the Terms of Service. # see: https://gitlab.com/gitlab-org/gitlab-foss/-/issues/45849 with self.assertRaises(OAuth2Error): # no id, 4xx code, raises _check_errors(response) def test_error_response(self): body = "403 Forbidden" response = MockedResponse(HTTPStatus.FORBIDDEN, body) with self.assertRaises(OAuth2Error): # no id, 4xx code, raises _check_errors(response) def test_invalid_response(self): response = MockedResponse(HTTPStatus.OK, json.dumps({})) with self.assertRaises(OAuth2Error): # No id, raises _check_errors(response) def test_bad_response(self): response = MockedResponse(HTTPStatus.BAD_REQUEST, json.dumps({})) with self.assertRaises(OAuth2Error): # bad json, raises _check_errors(response) def test_extra_data(self): self.login(self.get_mocked_response()) account = SocialAccount.objects.get(uid=str(self._uid)) self.assertEqual(account.extra_data["id"], self._uid) ================================================ FILE: tests/apps/socialaccount/providers/globus/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/globus/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from django.test.utils import override_settings from allauth.socialaccount.providers.globus.provider import GlobusProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class GlobusTests(OAuth2TestsMixin, TestCase): provider_id = GlobusProvider.id @override_settings(SOCIALACCOUNT_QUERY_EMAIL=True) def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "identity_provider_display_name": "University of Gozorpazorp", "sub": "a6fc81e-4a6c1-97ac-b4c6-84ff6a8ce662", "preferred_username": "morty@ugz.edu", "identity_provider": "9a4c8312f-9432-9a7c-1654-6a987c6531fa", "organization": "University of Gozorpazorp", "email": "morty@ugz.edu", "name": "Morty Smith" } """, ) def get_expected_to_str(self): return "morty@ugz.edu" ================================================ FILE: tests/apps/socialaccount/providers/google/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/google/tests.py ================================================ import json import time from http import HTTPStatus from importlib import import_module from unittest.mock import Mock, patch from django.conf import settings from django.contrib.auth.models import User from django.core import mail from django.test import TestCase from django.test.client import RequestFactory from django.test.utils import override_settings from django.urls import reverse import pytest from allauth.account import app_settings as account_settings from allauth.account.adapter import get_adapter as get_account_adapter from allauth.account.models import EmailAddress, EmailConfirmation from allauth.account.signals import user_signed_up from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.models import SocialAccount, SocialToken from allauth.socialaccount.providers.apple.client import jwt_encode from allauth.socialaccount.providers.google.provider import GoogleProvider from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import mocked_response @pytest.fixture def settings_with_google_provider(settings): settings.SOCIALACCOUNT_PROVIDERS = { "google": { "APP": { "client_id": "app123id", "key": "google", "secret": "dummy", } } } return settings @override_settings( SOCIALACCOUNT_AUTO_SIGNUP=True, ACCOUNT_SIGNUP_FORM_CLASS=None, ACCOUNT_EMAIL_VERIFICATION=account_settings.EmailVerificationMethod.MANDATORY, ) class GoogleTests(OAuth2TestsMixin, TestCase): provider_id = GoogleProvider.id def setUp(self): super().setUp() self.email = "raymond.penners@example.com" self.identity_overwrites = {} def get_expected_to_str(self): return "raymond.penners@example.com" def get_google_id_token_payload(self): now = int(time.time()) client_id = "app123id" # Matches `setup_app` payload = { "iss": "https://accounts.google.com", "azp": client_id, "aud": client_id, "sub": "108204268033311374519", "hd": "example.com", "email": self.email, "email_verified": True, "at_hash": "HK6E_P6Dh8Y93mRNtsDB1Q", "name": "Raymond Penners", "picture": "https://lh5.googleusercontent.com/photo.jpg", "given_name": "Raymond", "family_name": "Penners", "locale": "en", "iat": now, "exp": now + 60 * 60, } payload.update(self.identity_overwrites) return payload def get_login_response_json(self, with_refresh_token=True): data = { "access_token": "testac", "expires_in": 3600, "scope": "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile openid", "token_type": "Bearer", "id_token": jwt_encode(self.get_google_id_token_payload(), "secret"), } return json.dumps(data) @override_settings(SOCIALACCOUNT_AUTO_SIGNUP=False) def test_login(self): resp = self.login(resp_mock=None) self.assertRedirects(resp, reverse("socialaccount_signup")) def test_wrong_id_token_claim_values(self): wrong_claim_values = { "iss": "not-google", "exp": time.time() - 1, "aud": "foo", } template_ext = getattr(settings, "ACCOUNT_TEMPLATE_EXTENSION", "html") for key, value in wrong_claim_values.items(): with self.subTest(key): self.identity_overwrites = {key: value} resp = self.login(resp_mock=None) self.assertTemplateUsed( resp, f"socialaccount/authentication_error.{template_ext}" ) def test_username_based_on_email(self): self.identity_overwrites = {"given_name": "明", "family_name": "小"} self.login(resp_mock=None) user = User.objects.get(email=self.email) self.assertEqual(user.username, "raymond.penners") def test_email_verified(self): self.identity_overwrites = {"email_verified": True} self.login(resp_mock=None) email_address = EmailAddress.objects.get(email=self.email, verified=True) self.assertFalse( EmailConfirmation.objects.filter(email_address__email=self.email).exists() ) account = email_address.user.socialaccount_set.all()[0] self.assertEqual(account.extra_data["given_name"], "Raymond") def test_user_signed_up_signal(self): sent_signals = [] def on_signed_up(sender, request, user, **kwargs): sociallogin = kwargs["sociallogin"] self.assertEqual(sociallogin.account.provider, GoogleProvider.id) self.assertEqual(sociallogin.account.user, user) sent_signals.append(sender) user_signed_up.connect(on_signed_up) self.login(resp_mock=None) self.assertTrue(len(sent_signals) > 0) user_signed_up.disconnect(on_signed_up) @override_settings(ACCOUNT_EMAIL_CONFIRMATION_HMAC=False) def test_email_unverified(self): self.identity_overwrites = {"email_verified": False} resp = self.login(resp_mock=None) email_address = EmailAddress.objects.get(email=self.email) self.assertFalse(email_address.verified) self.assertTrue( EmailConfirmation.objects.filter(email_address__email=self.email).exists() ) self.assertTemplateUsed( resp, "account/email/email_confirmation_signup_subject.txt" ) def test_email_verified_stashed(self): # http://slacy.com/blog/2012/01/how-to-set-session-variables-in-django-unit-tests/ engine = import_module(settings.SESSION_ENGINE) store = engine.SessionStore() store.save() self.client.cookies[settings.SESSION_COOKIE_NAME] = store.session_key request = RequestFactory().get("/") request.session = self.client.session adapter = get_account_adapter() adapter.stash_verified_email(request, self.email) request.session.save() self.identity_overwrites = {"email_verified": False} self.login(resp_mock=None) email_address = EmailAddress.objects.get(email=self.email) self.assertTrue(email_address.verified) self.assertFalse( EmailConfirmation.objects.filter(email_address__email=self.email).exists() ) def test_account_connect(self): email = "user@example.com" user = User.objects.create(username="user", is_active=True, email=email) user.set_password("test") user.save() EmailAddress.objects.create(user=user, email=email, primary=True, verified=True) self.client.login(username=user.username, password="test") self.identity_overwrites = {"email": email, "email_verified": True} self.login(resp_mock=None, process="connect") # Check if we connected... self.assertTrue( SocialAccount.objects.filter(user=user, provider=GoogleProvider.id).exists() ) # For now, we do not pick up any new email addresses on connect self.assertEqual(EmailAddress.objects.filter(user=user).count(), 1) self.assertEqual(EmailAddress.objects.filter(user=user, email=email).count(), 1) @override_settings( ACCOUNT_EMAIL_VERIFICATION=account_settings.EmailVerificationMethod.MANDATORY, SOCIALACCOUNT_EMAIL_VERIFICATION=account_settings.EmailVerificationMethod.NONE, ) def test_social_email_verification_skipped(self): self.identity_overwrites = {"email_verified": False} self.login(resp_mock=None) email_address = EmailAddress.objects.get(email=self.email) self.assertFalse(email_address.verified) self.assertFalse( EmailConfirmation.objects.filter(email_address__email=self.email).exists() ) @override_settings( ACCOUNT_EMAIL_VERIFICATION=account_settings.EmailVerificationMethod.OPTIONAL, SOCIALACCOUNT_EMAIL_VERIFICATION=account_settings.EmailVerificationMethod.OPTIONAL, ) def test_social_email_verification_optional(self): self.identity_overwrites = {"email_verified": False} self.login(resp_mock=None) self.assertEqual(len(mail.outbox), 1) self.login(resp_mock=None) self.assertEqual(len(mail.outbox), 1) @override_settings( SOCIALACCOUNT_PROVIDERS={ "google": { "APP": { "client_id": "app123id", "key": "google", "secret": "dummy", } } } ) class AppInSettingsTests(GoogleTests): """ Run the same set of tests but without having a SocialApp entry. """ pass def test_login_by_token(db, client, settings_with_google_provider): client.cookies.load({"g_csrf_token": "csrf"}) with patch( "allauth.socialaccount.internal.jwtkit.jwt.get_unverified_header" ) as g_u_h: with mocked_response({"dummykid": "-----BEGIN CERTIFICATE-----"}): with patch( "allauth.socialaccount.internal.jwtkit.load_pem_x509_certificate" ) as load_pem: with patch( "allauth.socialaccount.internal.jwtkit.jwt.decode" ) as decode: decode.return_value = { "iss": "https://accounts.google.com", "aud": "client_id", "sub": "123sub", "hd": "example.com", "email": "raymond@example.com", "email_verified": True, "at_hash": "HK6E_P6Dh8Y93mRNtsDB1Q", "name": "Raymond Penners", "picture": "https://lh5.googleusercontent.com/photo.jpg", "given_name": "Raymond", "family_name": "Penners", "locale": "en", "iat": 123, "exp": 456, } g_u_h.return_value = { "alg": "RS256", "kid": "dummykid", "typ": "JWT", } pem = Mock() load_pem.return_value = pem pem.public_key.return_value = "key" resp = client.post( reverse("google_login_by_token"), {"credential": "dummy", "g_csrf_token": "csrf"}, ) assert resp.status_code == HTTPStatus.FOUND socialaccount = SocialAccount.objects.get(uid="123sub") assert socialaccount.user.email == "raymond@example.com" @pytest.mark.parametrize( "id_key,verified_key", [ ("id", "email_verified"), ("sub", "verified_email"), ], ) @pytest.mark.parametrize("verified", [False, True]) def test_extract_data( id_key, verified_key, verified, settings_with_google_provider, db ): data = { "email": "a@b.com", } data[id_key] = "123" data[verified_key] = verified provider = get_adapter().get_provider(None, GoogleProvider.id) assert provider.extract_uid(data) == "123" emails = provider.extract_email_addresses(data) assert len(emails) == 1 assert emails[0].verified == verified assert emails[0].email == "a@b.com" @pytest.mark.parametrize( "fetch_userinfo,id_token_has_picture,response,expected_uid, expected_picture", [ (True, True, {"id_token": "123"}, "uid-from-id-token", "pic-from-id-token"), (True, False, {"id_token": "123"}, "uid-from-id-token", "pic-from-userinfo"), (True, True, {"access_token": "123"}, "uid-from-userinfo", "pic-from-userinfo"), ], ) @pytest.mark.parametrize("did_fetch_access_token", [False, True]) def test_complete_login_variants( response, settings_with_google_provider, db, fetch_userinfo, expected_uid, expected_picture, id_token_has_picture, did_fetch_access_token, ): with patch.object( GoogleOAuth2Adapter, "_fetch_user_info", return_value={ "id": "uid-from-userinfo", "picture": "pic-from-userinfo", }, ): id_token = {"sub": "uid-from-id-token"} if id_token_has_picture: id_token["picture"] = "pic-from-id-token" with patch( "allauth.socialaccount.providers.google.views._verify_and_decode", return_value=id_token, ) as decode_mock: request = None app = None adapter = GoogleOAuth2Adapter(request) adapter.did_fetch_access_token = did_fetch_access_token adapter.fetch_userinfo = fetch_userinfo token = SocialToken() login = adapter.complete_login(request, app, token, response) assert login.account.uid == expected_uid assert login.account.extra_data["picture"] == expected_picture if not response.get("id_token"): assert not decode_mock.called else: assert decode_mock.call_args[1]["verify_signature"] == ( not did_fetch_access_token ) ================================================ FILE: tests/apps/socialaccount/providers/gumroad/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/gumroad/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.gumroad.provider import GumroadProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class GumroadTests(OAuth2TestsMixin, TestCase): provider_id = GumroadProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "success": true, "user": { "bio": "a sailor, a tailor", "name": "John Smith", "twitter_handle": null, "user_id": "G_-mnBf9b1j9A7a4ub4nFQ==", "email": "johnsmith@gumroad.com", "url": "https://gumroad.com/sailorjohn" } }""", ) def get_expected_to_str(self): return "johnsmith@gumroad.com" ================================================ FILE: tests/apps/socialaccount/providers/hubic/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/hubic/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.hubic.provider import HubicProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class HubicTests(OAuth2TestsMixin, TestCase): provider_id = HubicProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "email": "user@example.com", "firstname": "Test", "activated": true, "creationDate": "2014-04-17T17:04:01+02:00", "language": "en", "status": "ok", "offer": "25g", "lastname": "User" } """, ) def get_expected_to_str(self): return "user@example.com" def get_login_response_json(self, with_refresh_token=True): return '{\ "access_token": "testac",\ "expires_in": "3600",\ "refresh_token": "testrf",\ "token_type": "Bearer"\ }' ================================================ FILE: tests/apps/socialaccount/providers/hubspot/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/hubspot/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.hubspot.provider import HubspotProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class HubspotTests(OAuth2TestsMixin, TestCase): provider_id = HubspotProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "token": "CNye4dqFMBICAAEYhOKlDZZ_z6IVKI_xMjIUgmFsNQzgBjNE9YBmhAhNOtfN0ak6BAAAAEFCFIIwn2EVRLpvJI9hP4tbIeKHw7ZXSgNldTFSAFoA", "user": "m@acme.com", "hub_domain": "acme.com", "scopes": ["oauth"], "scope_to_scope_group_pks": [25, 31], "trial_scopes": [], "trial_scope_to_scope_group_pks": [], "hub_id": 211580, "app_id": 833572, "expires_in": 1799, "user_id": 42607123, "token_type": "access" }""", ) def get_expected_to_str(self): return "m@acme.com" ================================================ FILE: tests/apps/socialaccount/providers/instagram/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/instagram/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.instagram.provider import InstagramProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class InstagramTests(OAuth2TestsMixin, TestCase): provider_id = InstagramProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "username": "georgewhewell", "bio": "", "website": "", "profile_picture": "http://images.ak.instagram.com/profiles/profile_11428116_75sq_1339547159.jpg", "full_name": "georgewhewell", "counts": { "media": 74, "followed_by": 91, "follows": 104 }, "id": "11428116" }""", ) # noqa def get_expected_to_str(self): return "georgewhewell" ================================================ FILE: tests/apps/socialaccount/providers/jupyterhub/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/jupyterhub/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.jupyterhub.provider import JupyterHubProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class JupyterHubTests(OAuth2TestsMixin, TestCase): provider_id = JupyterHubProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "kind": "user", "name": "abc", "admin": false, "groups": [], "server": null, "pending": null, "created": "2016-12-06T18:30:50.297567Z", "last_activity": "2017-02-07T17:29:36.470236Z", "servers": null} """, ) def get_expected_to_str(self): return "abc" ================================================ FILE: tests/apps/socialaccount/providers/kakao/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/kakao/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.kakao.provider import KakaoProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class KakaoTests(OAuth2TestsMixin, TestCase): provider_id = KakaoProvider.id kakao_data = """ { "id": 123456789, "connected_at": "2022-04-11T01:45:28Z", "kakao_account": { "profile_nickname_needs_agreement": false, "profile_image_needs_agreement": false, "profile": { "nickname": "홍길동", "thumbnail_image_url": "http://yyy.kakao.com/.../img_110x110.jpg", "profile_image_url": "http://yyy.kakao.com/dn/.../img_640x640.jpg", "is_default_image":false, "is_default_nickname": false }, "name_needs_agreement":false, "name":"홍길동", "email_needs_agreement":false, "is_email_valid": true, "is_email_verified": true, "email": "sample@sample.com", "age_range_needs_agreement":false, "age_range":"20~29", "birthyear_needs_agreement": false, "birthyear": "2002", "birthday_needs_agreement":false, "birthday":"1130", "birthday_type":"SOLAR", "gender_needs_agreement":false, "gender":"female", "phone_number_needs_agreement": false, "phone_number": "+82 010-1234-5678", "ci_needs_agreement": false, "ci": "CI", "ci_authenticated_at": "2019-03-11T11:25:22Z" }, "properties":{ "CUSTOM_PROPERTY_KEY": "CUSTOM_PROPERTY_VALUE" }, "for_partner": { "uuid": "UUID" } } """ def get_expected_to_str(self): return "sample@sample.com" def get_mocked_response(self, data=None): if data is None: data = self.kakao_data return MockedResponse(HTTPStatus.OK, data) ================================================ FILE: tests/apps/socialaccount/providers/lemonldap/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/lemonldap/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.lemonldap.provider import LemonLDAPProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class LemonLDAPTests(OAuth2TestsMixin, TestCase): provider_id = LemonLDAPProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "email": "dwho@example.com", "sub": "dwho", "preferred_username": "dwho", "name": "Doctor Who" } """, ) def get_expected_to_str(self): return "dwho@example.com" ================================================ FILE: tests/apps/socialaccount/providers/lichess/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/lichess/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.lichess.provider import LichessProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class LichessTests(OAuth2TestsMixin, TestCase): provider_id = LichessProvider.id def get_mocked_response(self): return [ MockedResponse( HTTPStatus.OK, """ { "id": "george", "url": "https://lichess.org/@/george", "count": { "ai": 0, "me": 0, "all": 0, "win": 0, "draw": 0, "loss": 0, "winH": 0, "drawH": 0, "lossH": 0, "rated": 0, "import": 0, "playing": 0, "bookmark": 0 }, "perfs": { "blitz": { "rd": 500, "prog": 0, "prov": true, "games": 0, "rating": 1500 }, "rapid": { "rd": 500, "prog": 0, "prov": true, "games": 0, "rating": 1500 }, "bullet": { "rd": 500, "prog": 0, "prov": true, "games": 0, "rating": 1500 }, "classical": { "rd": 500, "prog": 0, "prov": true, "games": 0, "rating": 1500 }, "correspondence": { "rd": 500, "prog": 0, "prov": true, "games": 0, "rating": 1500 } }, "seenAt": 1713837454330, "blocking": false, "playTime": { "tv": 0, "total": 0 }, "username": "george", "createdAt": 1713837409417, "following": false, "followable": true, "followsYou": false } """, ), MockedResponse(HTTPStatus.OK, """{"email":"george@example.com"}"""), ] def get_expected_to_str(self): return "george" ================================================ FILE: tests/apps/socialaccount/providers/line/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/line/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.line.provider import LineProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class LineTests(OAuth2TestsMixin, TestCase): provider_id = LineProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "userId": "u7d47d26a6bab09b95695ff02d1a36e38", "displayName": "\uc774\uc0c1\ud601", "pictureUrl": "http://dl.profile.line-cdn.net/0m055ab14d725138288331268c45ac5286a35482fb794a" }""", ) def get_expected_to_str(self): return "\uc774\uc0c1\ud601" ================================================ FILE: tests/apps/socialaccount/providers/linkedin_oauth2/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/linkedin_oauth2/tests.py ================================================ from http import HTTPStatus from json import loads from django.test import TestCase from django.test.utils import override_settings from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.base import ProviderException from allauth.socialaccount.providers.linkedin_oauth2.provider import ( LinkedInOAuth2Provider, ) from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class LinkedInOAuth2Tests(OAuth2TestsMixin, TestCase): provider_id = LinkedInOAuth2Provider.id def get_mocked_response(self): return [ MockedResponse( HTTPStatus.OK, """ {} """, ), MockedResponse( HTTPStatus.OK, """ { "profilePicture": { "displayImage": "urn:li:digitalmediaAsset:12345abcdefgh-12abcd" }, "id": "1234567", "lastName": { "preferredLocale": { "language": "en", "country": "US" }, "localized": { "en_US": "Penners" } }, "firstName": { "preferredLocale": { "language": "en", "country": "US" }, "localized": { "en_US": "Raymond" } } } """, ), ] def get_expected_to_str(self): return "Raymond Penners" def test_data_to_str(self): data = { "emailAddress": "john@doe.org", "firstName": "John", "id": "a1b2c3d4e", "lastName": "Doe", "pictureUrl": "https://media.licdn.com/mpr/foo", "pictureUrls": { "_total": 1, "values": ["https://media.licdn.com/foo"], }, "publicProfileUrl": "https://www.linkedin.com/in/johndoe", } acc = SocialAccount(extra_data=data, provider="linkedin_oauth2") self.assertEqual(acc.get_provider_account().to_str(), "john@doe.org") def test_get_avatar_url_no_picture_setting(self): extra_data = """ { "profilePicture": { "displayImage": "urn:li:digitalmediaAsset:12345abcdefgh-12abcd" }, "id": "1234567", "lastName": { "preferredLocale": { "language": "en", "country": "US" }, "localized": { "en_US": "Penners" } }, "firstName": { "preferredLocale": { "language": "en", "country": "US" }, "localized": { "en_US": "Raymond" } } } """ acc = SocialAccount( extra_data=loads(extra_data), provider="linkedin_oauth2", ) self.assertIsNone(acc.get_avatar_url()) @override_settings( SOCIALACCOUNT_PROVIDERS={ "linkedin_oauth2": { "PROFILE_FIELDS": [ "id", "firstName", "lastName", "profilePicture(displayImage~:playableStreams)", ], "PROFILEPICTURE": { "display_size_w_h": (400, 400.0), }, }, } ) def test_get_avatar_url_with_setting(self): extra_data = """ { "profilePicture": { "displayImage": "urn:li:digitalmediaAsset:12345abcdefgh-12abcd" }, "id": "1234567", "lastName": { "preferredLocale": { "language": "en", "country": "US" }, "localized": { "en_US": "Penners" } }, "firstName": { "preferredLocale": { "language": "en", "country": "US" }, "localized": { "en_US": "Raymond" } } } """ acc = SocialAccount( extra_data=loads(extra_data), provider="linkedin_oauth2", ) self.assertIsNone(acc.get_avatar_url()) @override_settings( SOCIALACCOUNT_PROVIDERS={ "linkedin_oauth2": { "PROFILE_FIELDS": [ "id", "firstName", "lastName", "profilePicture(displayImage~:playableStreams)", ], "PROFILEPICTURE": { "display_size_w_h": (100, 100.0), }, }, } ) def test_get_avatar_url_with_picture(self): extra_data = """ { "profilePicture": { "displayImage": "urn:li:digitalmediaAsset:12345abcdefgh-12abcd" }, "id": "1234567", "lastName": { "preferredLocale": { "language": "en", "country": "US" }, "localized": { "en_US": "Penners" } }, "firstName": { "preferredLocale": { "language": "en", "country": "US" }, "localized": { "en_US": "Raymond" } }, "profilePicture": { "displayImage~": { "elements": [ { "authorizationMethod": "PUBLIC", "data": { "com.linkedin.digitalmedia.mediaartifact.StillImage": { "storageSize": { "height": 100, "width": 100 }, "storageAspectRatio": { "heightAspect": 1.0, "formatted": "1.00:1.00", "widthAspect": 1.0 }, "displaySize": { "height": 100.0, "width": 100.0, "uom": "PX" }, "rawCodecSpec": { "name": "jpeg", "type": "image" }, "displayAspectRatio": { "heightAspect": 1.0, "formatted": "1.00:1.00", "widthAspect": 1.0 }, "mediaType": "image/jpeg" } }, "artifact": "urn:li:digitalmediaMediaArtifact:avatar", "identifiers": [ { "identifierExpiresInSeconds": 4, "file": "urn:li:digitalmediaFile:this-is-the-link", "index": 0, "identifier": "this-is-the-link", "mediaType": "image/jpeg", "identifierType": "EXTERNAL_URL" } ] } ] } } } """ acc = SocialAccount( extra_data=loads(extra_data), provider="linkedin_oauth2", ) self.assertEqual("this-is-the-link", acc.get_avatar_url()) @override_settings( SOCIALACCOUNT_PROVIDERS={ "linkedin_oauth2": { "PROFILE_FIELDS": [ "id", "firstName", "lastName", "profilePicture(displayImage~:playableStreams)", ], "PROFILEPICTURE": { "display_size_w_h": (400, 400.0), }, }, } ) def test_get_avatar_url_size_mismatch(self): extra_data = """ { "profilePicture": { "displayImage": "urn:li:digitalmediaAsset:12345abcdefgh-12abcd" }, "id": "1234567", "lastName": { "preferredLocale": { "language": "en", "country": "US" }, "localized": { "en_US": "Penners" } }, "firstName": { "preferredLocale": { "language": "en", "country": "US" }, "localized": { "en_US": "Raymond" } }, "profilePicture": { "displayImage~": { "elements": [ { "authorizationMethod": "PUBLIC", "data": { "com.linkedin.digitalmedia.mediaartifact.StillImage": { "storageSize": { "height": 100, "width": 100 }, "storageAspectRatio": { "heightAspect": 1.0, "formatted": "1.00:1.00", "widthAspect": 1.0 }, "displaySize": { "height": 100.0, "width": 100.0, "uom": "PX" }, "rawCodecSpec": { "name": "jpeg", "type": "image" }, "displayAspectRatio": { "heightAspect": 1.0, "formatted": "1.00:1.00", "widthAspect": 1.0 }, "mediaType": "image/jpeg" } }, "artifact": "urn:li:digitalmediaMediaArtifact:avatar", "identifiers": [ { "identifierExpiresInSeconds": 4, "file": "urn:li:digitalmediaFile:this-is-the-link", "index": 0, "identifier": "this-is-the-link", "mediaType": "image/jpeg", "identifierType": "EXTERNAL_URL" } ] } ] } } } """ acc = SocialAccount( extra_data=loads(extra_data), provider="linkedin_oauth2", ) self.assertIsNone(acc.get_avatar_url()) @override_settings( SOCIALACCOUNT_PROVIDERS={ "linkedin_oauth2": { "PROFILE_FIELDS": [ "id", "firstName", "lastName", "profilePicture(displayImage~:playableStreams)", ], "PROFILEPICTURE": { "display_size_w_h": (400, 400.0), }, }, } ) def test_get_avatar_url_auth_mismatch(self): extra_data = """ { "profilePicture": { "displayImage": "urn:li:digitalmediaAsset:12345abcdefgh-12abcd" }, "id": "1234567", "lastName": { "preferredLocale": { "language": "en", "country": "US" }, "localized": { "en_US": "Penners" } }, "firstName": { "preferredLocale": { "language": "en", "country": "US" }, "localized": { "en_US": "Raymond" } }, "profilePicture": { "displayImage~": { "elements": [ { "authorizationMethod": "PRIVATE", "data": { "com.linkedin.digitalmedia.mediaartifact.StillImage": { "storageSize": { "height": 100, "width": 100 }, "storageAspectRatio": { "heightAspect": 1.0, "formatted": "1.00:1.00", "widthAspect": 1.0 }, "displaySize": { "height": 100.0, "width": 100.0, "uom": "PX" }, "rawCodecSpec": { "name": "jpeg", "type": "image" }, "displayAspectRatio": { "heightAspect": 1.0, "formatted": "1.00:1.00", "widthAspect": 1.0 }, "mediaType": "image/jpeg" } }, "artifact": "urn:li:digitalmediaMediaArtifact:avatar", "identifiers": [ { "identifierExpiresInSeconds": 4, "file": "urn:li:digitalmediaFile:this-is-the-link", "index": 0, "identifier": "this-is-the-link", "mediaType": "image/jpeg", "identifierType": "EXTERNAL_URL" } ] } ] } } } """ acc = SocialAccount( extra_data=loads(extra_data), provider="linkedin_oauth2", ) self.assertIsNone(acc.get_avatar_url()) @override_settings( SOCIALACCOUNT_PROVIDERS={ "linkedin_oauth2": { "PROFILE_FIELDS": [ "id", "firstName", "lastName", "profilePicture(displayImage~:playableStreams)", ], "PROFILEPICTURE": { "display_size_w_h": (100, 100), }, }, } ) def test_get_avatar_url_float_vs_int(self): extra_data = """ { "profilePicture": { "displayImage": "urn:li:digitalmediaAsset:12345abcdefgh-12abcd" }, "id": "1234567", "lastName": { "preferredLocale": { "language": "en", "country": "US" }, "localized": { "en_US": "Penners" } }, "firstName": { "preferredLocale": { "language": "en", "country": "US" }, "localized": { "en_US": "Raymond" } }, "profilePicture": { "displayImage~": { "elements": [ { "authorizationMethod": "PUBLIC", "data": { "com.linkedin.digitalmedia.mediaartifact.StillImage": { "storageSize": { "height": 100, "width": 100 }, "storageAspectRatio": { "heightAspect": 1.0, "formatted": "1.00:1.00", "widthAspect": 1.0 }, "displaySize": { "height": 100.0, "width": 100.0, "uom": "PX" }, "rawCodecSpec": { "name": "jpeg", "type": "image" }, "displayAspectRatio": { "heightAspect": 1.0, "formatted": "1.00:1.00", "widthAspect": 1.0 }, "mediaType": "image/jpeg" } }, "artifact": "urn:li:digitalmediaMediaArtifact:avatar", "identifiers": [ { "identifierExpiresInSeconds": 4, "file": "urn:li:digitalmediaFile:this-is-the-link", "index": 0, "identifier": "this-is-the-link", "mediaType": "image/jpeg", "identifierType": "EXTERNAL_URL" } ] } ] } } } """ acc = SocialAccount( extra_data=loads(extra_data), provider="linkedin_oauth2", ) self.assertEqual("this-is-the-link", acc.get_avatar_url()) def test_id_missing(self): extra_data = """ { "profilePicture": { "displayImage": "urn:li:digitalmediaAsset:12345abcdefgh-12abcd" }, "Id": "1234567" } """ self.assertRaises( ProviderException, self.provider.extract_uid, loads(extra_data) ) ================================================ FILE: tests/apps/socialaccount/providers/mailchimp/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/mailchimp/tests.py ================================================ """Test MailChimp OAuth2 v3 Flow.""" from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.mailchimp.provider import MailChimpProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class MailChimpTests(OAuth2TestsMixin, TestCase): """Test Class for MailChimp OAuth2 v3.""" provider_id = MailChimpProvider.id def get_mocked_response(self): """Test authentication with an non-null avatar.""" return MockedResponse( HTTPStatus.OK, """{ "dc": "usX", "role": "owner", "accountname": "Name can have spaces", "user_id": "99999999", "login": { "email": "test@example.com", "avatar": "http://gallery.mailchimp.com/1a1a/avatar/2a2a.png", "login_id": "88888888", "login_name": "test@example.com", "login_email": "test@example.com" }, "login_url": "https://login.mailchimp.com", "api_endpoint": "https://usX.api.mailchimp.com" }""", ) def get_expected_to_str(self): return "test@example.com" ================================================ FILE: tests/apps/socialaccount/providers/mailcow/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/mailcow/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.mailcow.provider import MailcowProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class MailcowTests(OAuth2TestsMixin, TestCase): provider_id = MailcowProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "success": true, "username": "user@example.com", "id": "user@example.com", "identifier": "user@example.com", "email": "user@example.com", "full_name": "Test User", "displayName": "Test User", "created": "2021-12-15 14:35:54", "modified": "2023-11-02 09:37:58", "active": 1 } """, ) def get_expected_to_str(self): return "user@example.com" ================================================ FILE: tests/apps/socialaccount/providers/mailru/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/mailru/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.mailru.provider import MailRuProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class MailRuTests(OAuth2TestsMixin, TestCase): provider_id = MailRuProvider.id def get_mocked_response(self, verified_email=True): return MockedResponse( HTTPStatus.OK, """ [ { "uid": "15410773191172635989", "first_name": "Евгений", "last_name": "Маслов", "nick": "maslov", "email": "emaslov@mail.ru", "sex": 0, "birthday": "15.02.1980", "has_pic": 1, "pic": "http://avt.appsmail.ru/mail/emaslov/_avatar", "pic_small": "http://avt.appsmail.ru/mail/emaslov/_avatarsmall", "pic_big": "http://avt.appsmail.ru/mail/emaslov/_avatarbig", "link": "http://my.mail.ru/mail/emaslov/", "referer_type": "", "referer_id": "", "is_online": 1, "friends_count": 145, "is_verified": 1, "vip" : 0, "app_installed": 1, "location": { "country": { "name": "Россия", "id": "24" }, "city": { "name": "Москва", "id": "25" }, "region": { "name": "Москва", "id": "999999" } } }]""", ) # noqa def get_login_response_json(self, with_refresh_token=True): # TODO: This is not an actual response. I added this in order # to get the test suite going but did not verify to check the # exact response being returned. return '{"access_token": "testac", "uid": "weibo", "refresh_token": "testrf", "x_mailru_vid": "1"}' # noqa def get_expected_to_str(self): return "emaslov@mail.ru" ================================================ FILE: tests/apps/socialaccount/providers/mediawiki/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/mediawiki/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.mediawiki.provider import MediaWikiProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class MediaWikiTests(OAuth2TestsMixin, TestCase): provider_id = MediaWikiProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "iss": "https://meta.wikimedia.org", "sub": 12345, "aud": "1234567890abcdef", "exp": 946681300, "iat": 946681200, "username": "John Doe", "editcount": 123, "confirmed_email": true, "blocked": false, "registered": "20000101000000", "groups": ["*", "user", "autoconfirmed"], "rights": ["read", "edit"], "grants": ["basic"], "email": "johndoe@example.com" } """, ) def get_expected_to_str(self): return "John Doe" ================================================ FILE: tests/apps/socialaccount/providers/meetup/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/meetup/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.meetup.provider import MeetupProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class MeetupTests(OAuth2TestsMixin, TestCase): provider_id = MeetupProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ {"id": 1, "lang": "en_US", "city": "Bhubaneswar", "photo": { "thumb_link":"", "photo_id": 240057062, "highres_link":"", "base_url": "http://photos2.meetupstatic.com", "type": "member", "name": "Abhishek Jaiswal", "other_services": {}, "country": "in", "topics": [{"name": "Open Source", "urlkey": "opensource", "id": 563}, {"name": "Python", "urlkey": "python", "id": 1064}, {"name": "Software Development", "urlkey": "softwaredev", "id": 3833}, {"name": "Computer programming", "urlkey": "computer-programming", "id": 48471}, {"name": "Python Web Development", "urlkey": "python-web-development", "id": 917242}, {"name": "Data Science using Python", "urlkey": "data-science-using-python", "id": 1481522}], "lon": 85.83999633789062, "joined": 1411642310000, "id": 173662372, "status": "active", "link": "http://www.meetup.com/members/173662372", "hometown": "Kolkata", "lat": 20.270000457763672, "visited": 1488829924000, "self": {"common": {}}}}""", ) def get_expected_to_str(self): return "Meetup" ================================================ FILE: tests/apps/socialaccount/providers/microsoft/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/microsoft/tests.py ================================================ import json from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.microsoft.provider import MicrosoftGraphProvider from allauth.socialaccount.providers.microsoft.views import _check_errors from allauth.socialaccount.providers.oauth2.client import OAuth2Error from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class MicrosoftGraphTests(OAuth2TestsMixin, TestCase): provider_id = MicrosoftGraphProvider.id def get_mocked_response(self): response_data = """ { "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity", "id": "16f5a7b6-5a15-4568-aa5a-31bb117e9967", "businessPhones": [], "displayName": "Anne Weiler", "givenName": "Anne", "jobTitle": "Manufacturing Lead", "mail": "annew@CIE493742.onmicrosoft.com", "mobilePhone": "+1 3528700812", "officeLocation": null, "preferredLanguage": "en-US", "surname": "Weiler", "userPrincipalName": "annew@CIE493742.onmicrosoft.com", "mailNickname": "annew" } """ # noqa return MockedResponse(HTTPStatus.OK, response_data) def get_expected_to_str(self): return "annew@CIE493742.onmicrosoft.com" def test_invalid_data(self): response = MockedResponse(HTTPStatus.OK, json.dumps({})) with self.assertRaises(OAuth2Error): # No id, raises _check_errors(response) def test_profile_invalid_response(self): data = { "error": { "code": "InvalidAuthenticationToken", "message": "Access token validation failure. Invalid audience.", } } response = MockedResponse(HTTPStatus.UNAUTHORIZED, json.dumps(data)) with self.assertRaises(OAuth2Error): # no id, 4xx code, raises with message _check_errors(response) def test_invalid_response(self): response = MockedResponse(HTTPStatus.OK, "invalid json data") with self.assertRaises(OAuth2Error): # bad json, raises _check_errors(response) ================================================ FILE: tests/apps/socialaccount/providers/miro/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/miro/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.miro.provider import MiroProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class MiroTests(OAuth2TestsMixin, TestCase): provider_id = MiroProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "type" : "user", "id" : "5298357290348572584", "name" : "Pavel Oborin", "createdAt" : "2017-03-23T09:41:01Z", "role" : "developer", "email" : "oborin.p@gmail.com", "state" : "registered", "picture" : { "type" : "picture", "id" : "Optional[5289752983457238457]", "imageUrl" : "https://r.miro.com/some/image" } }""", ) def get_expected_to_str(self): return "oborin.p@gmail.com" ================================================ FILE: tests/apps/socialaccount/providers/naver/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/naver/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.naver.provider import NaverProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class NaverTests(OAuth2TestsMixin, TestCase): provider_id = NaverProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "response": { "enc_id": "46111c25f969116de4e545f13a415bb5383db2a79782da8851db72b2cced3180", "nickname": "\ubc31\ud638", "profile_image": "https://ssl.pstatic.net/static/pwe/address/nodata_33x33.gif", "gender": "M", "id": "7163491", "age": "20-29", "birthday": "03-22", "email": "shlee940322@example.com", "name": "\uc774\uc0c1\ud601" }, "message": "success", "resultcode": "00" } """, ) def get_expected_to_str(self): return "shlee940322@example.com" ================================================ FILE: tests/apps/socialaccount/providers/netiq/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/netiq/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.netiq.provider import NetIQProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class NetIQTests(OAuth2TestsMixin, TestCase): provider_id = NetIQProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "sub": "d4c094dd899ab0408fb9d4c094dd899a", "acr": "secure/name/password/uri", "preferred_username": "Mocktest", "email": "mocktest@your.netiq.server.example.com", "nickname": "Mocktest", "family_name": "test", "given_name": "Mock", "website": "https://www.exanple.com" } """, ) def get_expected_to_str(self): return "mocktest@your.netiq.server.example.com" ================================================ FILE: tests/apps/socialaccount/providers/nextcloud/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/nextcloud/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from django.test.utils import override_settings from allauth.socialaccount.providers.nextcloud.provider import NextCloudProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse @override_settings( SOCIALACCOUNT_PROVIDERS={"nextcloud": {"SERVER": "https://nextcloud.example.org"}} ) class NextCloudTests(OAuth2TestsMixin, TestCase): provider_id = NextCloudProvider.id def get_login_response_json(self, with_refresh_token=True): return ( super() .get_login_response_json(with_refresh_token=with_refresh_token) .replace("uid", "user_id") ) def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "ocs": { "meta": { "status": "ok", "statuscode": 100, "message": "OK", "totalitems": "", "itemsperpage": "" }, "data": { "enabled": true, "storageLocation": "\\/var\\/www\\/html\\/data\\/pennersr", "id": "pennersr", "lastLogin": 1730973409000, "backend": "Database", "subadmin": [], "quota": { "free": 9159623057408, "used": 1585107741, "total": 9161208165149, "relative": 0.02, "quota": -3 }, "email": "batman@wayne.com", "displayname": "pennersr", "phone": "", "address": "", "website": "", "twitter": "", "groups": [ "admin" ], "language": "nl", "locale": "" } } } """, ) def get_expected_to_str(self): return "batman@wayne.com" ================================================ FILE: tests/apps/socialaccount/providers/notion/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/notion/tests.py ================================================ from http import HTTPStatus from urllib.parse import parse_qs, urlparse from django.test import TestCase from django.urls import reverse from django.utils.http import urlencode from allauth.socialaccount.providers.notion.provider import NotionProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse, mocked_response class NotionTests(OAuth2TestsMixin, TestCase): provider_id = NotionProvider.id pkce_enabled_default = False # Notion does not support PKCE. def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "workspace_id": "workspace-abc", "workspace_name": "My Workspace", "owner": { "user": { "id": "test123", "name": "John Doe", "avatar_url": "", "person": {"email": "john@example.com"}, } }, } """, ) # noqa def get_expected_to_str(self): return "John Doe (My Workspace)" def get_login_response_json(self, with_refresh_token=False): """ Docs here: https://developers.notion.com/docs/authorization#step-4-notion-responds-with-an-access_token-and-additional-information """ return """ { "access_token": "testac", "bot_id": "bot-abc", "duplicated_template_id": "template-abc", "owner": { "workspace_id": "workspace-abc", "user": { "id": "test123", "name": "John Doe", "avatar_url": "", "person": { "email": "john@example.com" } } }, "workspace_icon": "https://example.com/icon.png", "workspace_id": "workspace-abc", "workspace_name": "My Workspace" } """ def login(self, resp_mock=None, process="login", with_refresh_token=True): resp = self.client.post( f"{reverse(f'{self.provider.id}_login')}?{urlencode(dict(process=process))}" ) p = urlparse(resp["location"]) q = parse_qs(p.query) complete_url = reverse(f"{self.provider.id}_callback") response_json = self.get_login_response_json( with_refresh_token=with_refresh_token ) if isinstance(resp_mock, list): resp_mocks = resp_mock elif resp_mock is None: resp_mocks = [] else: resp_mocks = [resp_mock] with mocked_response( MockedResponse( HTTPStatus.OK, response_json, {"content-type": "application/json"} ), *resp_mocks, ): resp = self.client.get(complete_url, self.get_complete_parameters(q)) return resp ================================================ FILE: tests/apps/socialaccount/providers/oauth2/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/oauth2/tests/test_views.py ================================================ import copy from django.urls import reverse import pytest from pytest_django.asserts import assertTemplateUsed from allauth.socialaccount.adapter import get_adapter @pytest.mark.parametrize( "samesite_strict,did_already_redirect,expect_redirect", [ (True, False, True), (True, True, False), (False, False, False), ], ) def test_samesite_strict( client, samesite_strict, settings, google_provider_settings, did_already_redirect, expect_redirect, db, ): settings.SESSION_COOKIE_SAMESITE = "Strict" if samesite_strict else "Lax" query = "?state=123" resp = client.get( reverse("google_callback") + query + ("&_redir" if did_already_redirect else "") ) if expect_redirect: assertTemplateUsed(resp, "socialaccount/login_redirect.html") assert ( resp.context["redirect_to"] == f"{reverse('google_callback')}{query}&_redir=" ) else: assertTemplateUsed(resp, "socialaccount/authentication_error.html") @pytest.mark.parametrize("pkce_enabled", [False, True]) def test_config_from_app_settings( google_provider_settings, rf, db, settings, pkce_enabled ): settings.SOCIALACCOUNT_PROVIDERS = copy.deepcopy(settings.SOCIALACCOUNT_PROVIDERS) settings.SOCIALACCOUNT_PROVIDERS["google"]["APPS"][0]["settings"] = { "scope": ["this", "that"], "auth_params": {"x": "y"}, "oauth_pkce_enabled": pkce_enabled, } settings.SOCIALACCOUNT_PROVIDERS["google"]["SCOPE"] = ["not-this"] settings.SOCIALACCOUNT_PROVIDERS["google"]["AUTH_PARAMS"] = {"not": "this"} provider = get_adapter().get_provider(rf.get("/"), "google") assert provider.get_scope() == ["this", "that"] assert provider.get_auth_params() == {"x": "y"} assert ("code_verifier" in provider.get_pkce_params().keys()) == pkce_enabled @pytest.mark.parametrize("pkce_enabled", [False, True]) def test_config_from_provider_config( google_provider_settings, rf, db, settings, pkce_enabled ): settings.SOCIALACCOUNT_PROVIDERS = copy.deepcopy(settings.SOCIALACCOUNT_PROVIDERS) settings.SOCIALACCOUNT_PROVIDERS["google"]["SCOPE"] = ["some-scope"] settings.SOCIALACCOUNT_PROVIDERS["google"]["AUTH_PARAMS"] = {"auth": "param"} settings.SOCIALACCOUNT_PROVIDERS["google"]["OAUTH_PKCE_ENABLED"] = pkce_enabled provider = get_adapter().get_provider(rf.get("/"), "google") assert provider.get_scope() == ["some-scope"] assert provider.get_auth_params() == {"auth": "param"} assert ("code_verifier" in provider.get_pkce_params().keys()) == pkce_enabled ================================================ FILE: tests/apps/socialaccount/providers/odnoklassniki/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/odnoklassniki/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.odnoklassniki.provider import OdnoklassnikiProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class OdnoklassnikiTests(OAuth2TestsMixin, TestCase): provider_id = OdnoklassnikiProvider.id def get_mocked_response(self, verified_email=True): return MockedResponse( HTTPStatus.OK, """ {"uid":"561999209121","birthday":"1999-09-09","age":33,"first_name":"Ivan", "last_name":"Petrov","name":"Ivan Petrov","locale":"en","gender":"male", "has_email":true,"location":{"city":"Moscow","country":"RUSSIAN_FEDERATION", "countryCode":"RU","countryName":"Russia"},"online":"web","pic_1": "http://i500.mycdn.me/res/stub_50x50.gif", "pic_2":"http://usd1.mycdn.me/res/stub_128x96.gif"} """, ) def get_expected_to_str(self): return "Ivan Petrov" def get_login_response_json(self, with_refresh_token=True): return '{"access_token": "testac"}' # noqa ================================================ FILE: tests/apps/socialaccount/providers/okta/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/okta/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.okta.provider import OktaProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class OktaTests(OAuth2TestsMixin, TestCase): provider_id = OktaProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "sub": "00u33ow83pjQpCQJr1j8", "name": "Jon Smith", "locale": "AE", "email": "jsmith@example.com", "nickname": "Jon Smith", "preferred_username": "jsmith@example.com", "given_name": "Jon", "family_name": "Smith", "zoneinfo": "America/Los_Angeles", "updated_at": 1601285210, "email_verified": true } """, ) def get_expected_to_str(self): return "jsmith@example.com" ================================================ FILE: tests/apps/socialaccount/providers/openid/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/openid/tests.py ================================================ import urllib.error from http import HTTPStatus from unittest.mock import Mock, patch from django.conf import settings from django.contrib.auth import get_user_model from django.test import override_settings from django.urls import reverse import pytest from openid import fetchers from openid.consumer import consumer from openid.message import InvalidOpenIDNamespace from pytest_django.asserts import assertTemplateUsed from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.openid import views from allauth.socialaccount.providers.openid.utils import AXAttribute class TestFetcher(fetchers.Urllib2Fetcher): def fetch(self, url, body=None, headers=None): if url == "https://steamcommunity.com/openid": return fetchers.HTTPResponse( final_url="https://steamcommunity.com/openid", status=HTTPStatus.OK, headers={"content-type": "application/xrds+xml;charset=utf-8"}, body='\n\n\t\n\t\t\n\t\t\thttp://specs.openid.net/auth/2.0/server\t\t\n\t\t\thttps://steamcommunity.com/openid/login\n\t\t\n\t\n', ) if url == "https://steamcommunity.com/openid/login": return fetchers.HTTPResponse( final_url="https://steamcommunity.com/openid/login", status=HTTPStatus.OK, headers={"content-type": "text/plain;charset=utf-8"}, body="ns:http://specs.openid.net/auth/2.0\nerror_code:unsupported-type\nerror:Associations not supported\n", ) if url == "https://discovery-failure.com/": raise urllib.error.URLError ret = super().fetch(url, body=body, headers=headers) breakpoint() return ret @pytest.fixture(autouse=True) def setup_fetcher(): old_fetcher = fetchers.getDefaultFetcher() fetchers.setDefaultFetcher(TestFetcher()) yield fetchers.setDefaultFetcher(old_fetcher) def test_discovery_failure(client): """ This used to generate a server 500: DiscoveryFailure: No usable OpenID services found for http://www.google.com/ """ resp = client.post( reverse("openid_login"), dict(openid="https://discovery-failure.com/") ) assert "openid" in resp.context["form"].errors def test_login(client, db): # Location: https://s.yimg.com/wm/mbr/html/openid-eol-0.0.1.html resp = client.post( reverse(views.login), dict(openid="https://steamcommunity.com/openid") ) assert "steamcommunity.com/openid/login" in resp["location"] with patch( "allauth.socialaccount.providers.openid.views._openid_consumer" ) as consumer_mock: consumer_client = Mock() complete = Mock() consumer_mock.return_value = consumer_client consumer_client.complete = complete complete_response = Mock() complete.return_value = complete_response complete_response.status = consumer.SUCCESS complete_response.identity_url = "http://dummy/john/" with patch( "allauth.socialaccount.providers.openid.utils.SRegResponse" ) as sr_mock: with patch( "allauth.socialaccount.providers.openid.utils.FetchResponse" ) as fr_mock: sreg_mock = Mock() ax_mock = Mock() sr_mock.fromSuccessResponse = sreg_mock fr_mock.fromSuccessResponse = ax_mock sreg_mock.return_value = {} ax_mock.return_value = {AXAttribute.PERSON_FIRST_NAME: ["raymond"]} resp = client.post(reverse("openid_callback")) assert resp["location"] == "/accounts/profile/" get_user_model().objects.get(first_name="raymond") social_account = SocialAccount.objects.get( uid=complete_response.identity_url ) account = social_account.get_provider_account() assert account.to_str() == complete_response.identity_url @override_settings( SOCIALACCOUNT_PROVIDERS={ "openid": { "SERVERS": [ dict( id="yahoo", name="Yahoo", openid_url="http://me.yahoo.com", extra_attributes=[ ( "phone", "http://axschema.org/contact/phone/default", True, ) ], ) ] } } ) def test_login_with_extra_attributes(client, db): with patch("allauth.socialaccount.providers.openid.views.QUERY_EMAIL", True): resp = client.post( reverse(views.login), dict(openid="https://steamcommunity.com/openid") ) assert "steamcommunity.com/openid/login" in resp["location"] with patch( "allauth.socialaccount.providers.openid.views._openid_consumer" ) as consumer_mock: consumer_client = Mock() complete = Mock() endpoint = Mock() consumer_mock.return_value = consumer_client consumer_client.complete = complete complete_response = Mock() complete.return_value = complete_response complete_response.endpoint = endpoint complete_response.endpoint.server_url = "http://me.yahoo.com" complete_response.status = consumer.SUCCESS complete_response.identity_url = "http://dummy/john/" with patch( "allauth.socialaccount.providers.openid.utils.SRegResponse" ) as sr_mock: with patch( "allauth.socialaccount.providers.openid.utils.FetchResponse" ) as fr_mock: sreg_mock = Mock() ax_mock = Mock() sr_mock.fromSuccessResponse = sreg_mock fr_mock.fromSuccessResponse = ax_mock sreg_mock.return_value = {} ax_mock.return_value = { AXAttribute.CONTACT_EMAIL: ["raymond@example.com"], AXAttribute.PERSON_FIRST_NAME: ["raymond"], "http://axschema.org/contact/phone/default": ["123456789"], } resp = client.post(reverse("openid_callback")) assert resp["location"] == "/accounts/profile/" socialaccount = SocialAccount.objects.get(user__first_name="raymond") assert socialaccount.extra_data.get("phone") == "123456789" def test_callback_error(client, db): with patch( "allauth.socialaccount.providers.openid.views._openid_consumer" ) as consumer_mock: consumer_client = Mock() complete = Mock() consumer_mock.return_value = consumer_client consumer_client.complete = complete def raise_invalid_openid_namespace(*args, **kwargs): raise InvalidOpenIDNamespace("Invalid OpenID Namespace 'http://evil.com'") complete.side_effect = raise_invalid_openid_namespace template_ext = getattr(settings, "ACCOUNT_TEMPLATE_EXTENSION", "html") with patch( "allauth.socialaccount.providers.openid.utils.SRegResponse" ) as sr_mock: with patch( "allauth.socialaccount.providers.openid.utils.FetchResponse" ) as fr_mock: sreg_mock = Mock() ax_mock = Mock() sr_mock.fromSuccessResponse = sreg_mock fr_mock.fromSuccessResponse = ax_mock sreg_mock.return_value = {} ax_mock.return_value = {AXAttribute.PERSON_FIRST_NAME: ["raymond"]} resp = client.post(reverse("openid_callback")) assertTemplateUsed( resp, f"socialaccount/authentication_error.{template_ext}" ) ================================================ FILE: tests/apps/socialaccount/providers/openid_connect/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/openid_connect/tests.py ================================================ from django.test import TestCase import pytest from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.openid_connect.provider import ( OpenIDConnectProviderAccount, ) from tests.apps.socialaccount.base import OpenIDConnectTests class OpenIDConnectFetchUserInfoTests(OpenIDConnectTests, TestCase): provider_id = "oidc-server" class OpenIDConnectUseIDTokenTests(OpenIDConnectTests, TestCase): provider_id = "other-oidc-server" def setup_provider(self): super().setup_provider() self.app.settings["fetch_userinfo"] = False self.app.save() self.extra_data = self.id_token @pytest.mark.parametrize( "extra_data,expected_to_str", [ ({"username": "compatpre6511"}, "compatpre6511"), ({"id_token": {"username": "idtokusr"}}, "idtokusr"), ({"userinfo": {"username": "userinfousr"}}, "userinfousr"), ], ) def test_socialaccount_extra_data(extra_data, expected_to_str): sa = SocialAccount() sa.extra_data = extra_data assert OpenIDConnectProviderAccount(sa).to_str() == expected_to_str ================================================ FILE: tests/apps/socialaccount/providers/openstreetmap/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/openstreetmap/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.openstreetmap.provider import OpenStreetMapProvider from tests.apps.socialaccount.base import OAuthTestsMixin from tests.mocking import MockedResponse class OpenStreetMapTests(OAuthTestsMixin, TestCase): provider_id = OpenStreetMapProvider.id def get_mocked_response(self): return [ MockedResponse( HTTPStatus.OK, r""" { "version": "0.6", "generator": "OpenStreetMap server", "copyright": "OpenStreetMap and contributors", "attribution": "http://www.openstreetmap.org/copyright", "license": "http://opendatacommons.org/licenses/odbl/1-0/", "user": { "id": 1, "display_name": "Steve", "account_created": "2024-11-06T20:11:01Z", "description": "", "contributor_terms": { "agreed": true, "pd": true }, "img": { "href": "https://secure.gravatar.com/avatar.jpg" }, "roles": [], "changesets": { "count": 0 }, "traces": { "count": 0 }, "blocks": { "received": { "count": 0, "active": 0 } }, "languages": [ "en-US", "en" ], "messages": { "received": { "count": 0, "unread": 0 }, "sent": { "count": 0 } } } } """, ) ] # noqa def get_expected_to_str(self): return "Steve" def test_login(self): super().test_login() account = SocialAccount.objects.get(uid="1") osm_account = account.get_provider_account() self.assertEqual(osm_account.get_username(), "Steve") self.assertEqual( osm_account.get_avatar_url(), "https://secure.gravatar.com/avatar.jpg", ) self.assertEqual( osm_account.get_profile_url(), "https://www.openstreetmap.org/user/Steve", ) self.assertEqual(osm_account.to_str(), "Steve") ================================================ FILE: tests/apps/socialaccount/providers/orcid/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/orcid/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.orcid.provider import OrcidProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class OrcidTests(OAuth2TestsMixin, TestCase): provider_id = OrcidProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "orcid-identifier": { "uri": "https://sandbox.orcid.org/0000-0001-6796-198X", "path": "0000-0001-6796-198X", "host": "sandbox.orcid.org" }, "preferences": { "locale": "EN" }, "history": { "creation-method": "MEMBER_REFERRED", "completion-date": null, "submission-date": { "value": 1456951327337 }, "last-modified-date": { "value": 1519493486728 }, "claimed": true, "source": null, "deactivation-date": null, "verified-email": true, "verified-primary-email": true }, "person": { "last-modified-date": { "value": 1519493469738 }, "name": { "created-date": { "value": 1460669254582 }, "last-modified-date": { "value": 1460669254582 }, "given-names": { "value": "Patricia" }, "family-name": { "value": "Lawrence" }, "credit-name": null, "source": null, "visibility": "PUBLIC", "path": "0000-0001-6796-198X" }, "other-names": { "last-modified-date": null, "other-name": [], "path": "/0000-0001-6796-198X/other-names" }, "biography": { "created-date": { "value": 1460669254583 }, "last-modified-date": { "value": 1460669254583 }, "content": null, "visibility": "PUBLIC", "path": "/0000-0001-6796-198X/biography" }, "researcher-urls": { "last-modified-date": null, "researcher-url": [], "path": "/0000-0001-6796-198X/researcher-urls" }, "emails": { "last-modified-date": { "value": 1519493469738 }, "email": [ { "created-date": { "value": 1456951327661 }, "last-modified-date": { "value": 1519493469738 }, "source": { "source-orcid": { "uri": "https://sandbox.orcid.org/0000-0001-6796-198X", "path": "0000-0001-6796-198X", "host": "sandbox.orcid.org" }, "source-client-id": null, "source-name": { "value": "Patricia Lawrence" } }, "email": "lawrencepatricia@mailinator.com", "path": null, "visibility": "PUBLIC", "verified": true, "primary": true, "put-code": null } ], "path": "/0000-0001-6796-198X/email" }, "addresses": { "last-modified-date": null, "address": [], "path": "/0000-0001-6796-198X/address" }, "keywords": { "last-modified-date": null, "keyword": [], "path": "/0000-0001-6796-198X/keywords" }, "external-identifiers": { "last-modified-date": null, "external-identifier": [], "path": "/0000-0001-6796-198X/external-identifiers" }, "path": "/0000-0001-6796-198X/person" }, "activities-summary": { "last-modified-date": { "value": 1513777479628 }, "educations": { "last-modified-date": { "value": 1459957293365 }, "education-summary": [ { "created-date": { "value": 1459957293365 }, "last-modified-date": { "value": 1459957293365 }, "source": { "source-orcid": { "uri": "https://sandbox.orcid.org/0000-0001-6796-198X", "path": "0000-0001-6796-198X", "host": "sandbox.orcid.org" }, "source-client-id": null, "source-name": { "value": "Patricia Lawrence" } }, "department-name": null, "role-title": null, "start-date": null, "end-date": null, "organization": { "name": "Polytech'Rambouillet", "address": { "city": "Rambouillet", "region": null, "country": "FR" }, "disambiguated-organization": null }, "visibility": "PUBLIC", "put-code": 19996, "path": "/0000-0001-6796-198X/education/19996" } ], "path": "/0000-0001-6796-198X/educations" }, "employments": { "last-modified-date": { "value": 1513777479628 }, "employment-summary": [ { "created-date": { "value": 1510399314937 }, "last-modified-date": { "value": 1513777479628 }, "source": { "source-orcid": { "uri": "https://sandbox.orcid.org/0000-0001-6796-198X", "path": "0000-0001-6796-198X", "host": "sandbox.orcid.org" }, "source-client-id": null, "source-name": { "value": "Patricia Lawrence" } }, "department-name": null, "role-title": null, "start-date": { "year": { "value": "2015" }, "month": { "value": "03" }, "day": { "value": "02" } }, "end-date": null, "organization": { "name": "École nationale supérieure de céramique industrielle", "address": { "city": "Limoges", "region": null, "country": "FR" }, "disambiguated-organization": { "disambiguated-organization-identifier": "105362", "disambiguation-source": "RINGGOLD" } }, "visibility": "PUBLIC", "put-code": 29138, "path": "/0000-0001-6796-198X/employment/29138" }, { "created-date": { "value": 1502366640610 }, "last-modified-date": { "value": 1513777467282 }, "source": { "source-orcid": { "uri": "https://sandbox.orcid.org/0000-0001-6796-198X", "path": "0000-0001-6796-198X", "host": "sandbox.orcid.org" }, "source-client-id": null, "source-name": { "value": "Patricia Lawrence" } }, "department-name": null, "role-title": null, "start-date": { "year": { "value": "2002" }, "month": { "value": "02" }, "day": { "value": "16" } }, "end-date": { "year": { "value": "2015" }, "month": { "value": "08" }, "day": { "value": "12" } }, "organization": { "name": "University of Cambridge", "address": { "city": "Cambridge", "region": "Cambridgeshire", "country": "GB" }, "disambiguated-organization": { "disambiguated-organization-identifier": "2152", "disambiguation-source": "RINGGOLD" } }, "visibility": "PUBLIC", "put-code": 27562, "path": "/0000-0001-6796-198X/employment/27562" } ], "path": "/0000-0001-6796-198X/employments" }, "fundings": { "last-modified-date": null, "group": [], "path": "/0000-0001-6796-198X/fundings" }, "peer-reviews": { "last-modified-date": null, "group": [], "path": "/0000-0001-6796-198X/peer-reviews" }, "works": { "last-modified-date": { "value": 1459957753077 }, "group": [ { "last-modified-date": { "value": 1459957753077 }, "external-ids": { "external-id": [] }, "work-summary": [ { "put-code": 583440, "created-date": { "value": 1459957753047 }, "last-modified-date": { "value": 1459957753077 }, "source": { "source-orcid": { "uri": "https://sandbox.orcid.org/0000-0001-6796-198X", "path": "0000-0001-6796-198X", "host": "sandbox.orcid.org" }, "source-client-id": null, "source-name": { "value": "Patricia Lawrence" } }, "title": { "title": { "value": "Standard & Poor's fiscal methodology reviewed" }, "subtitle": null, "translated-title": null }, "external-ids": { "external-id": [] }, "type": "JOURNAL_ARTICLE", "publication-date": { "year": { "value": "2001" }, "month": { "value": "07" }, "day": { "value": "14" }, "media-type": null }, "visibility": "PUBLIC", "path": "/0000-0001-6796-198X/work/583440", "display-index": "0" } ] } ], "path": "/0000-0001-6796-198X/works" }, "path": "/0000-0001-6796-198X/activities" }, "path": "/0000-0001-6796-198X" } """, ) def get_expected_to_str(self): return "Orcid.org" def get_login_response_json(self, with_refresh_token=True): # TODO: This is not an actual response. I added this in order # to get the test suite going but did not verify to check the # exact response being returned. return """ { "access_token": "testac", "expires_in": 631138026, "token_type": "bearer", "orcid": "0000-0001-6796-198X", "scope": "/orcid-profile/read-limited", "refresh_token": "testrf" }""" ================================================ FILE: tests/apps/socialaccount/providers/patreon/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/patreon/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.patreon.provider import PatreonProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class PatreonTests(OAuth2TestsMixin, TestCase): provider_id = PatreonProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "data": { "relationships": { "pledges": { "data": [{ "type": "pledge", "id": "123456" }] } }, "attributes": { "last_name": "Interwebs", "is_suspended": false, "has_password": true, "full_name": "John Interwebs", "is_nuked": false, "first_name": "John", "social_connections": { "spotify": null, "discord": null, "twitter": null, "youtube": null, "facebook": null, "deviantart": null, "twitch": null }, "twitter": null, "is_email_verified": true, "facebook_id": null, "email": "john@example.com", "facebook": null, "thumb_url": "https://c8.patreon.com/100/123456", "vanity": null, "about": null, "is_deleted": false, "created": "2017-05-05T05:16:34+00:00", "url": "https://www.patreon.com/user?u=123456", "gender": 0, "youtube": null, "discord_id": null, "image_url": "https://c8.patreon.com/400/123456", "twitch": null }, "type": "user", "id": "123456" } }""", ) # noqa def get_expected_to_str(self): return "john@example.com" ================================================ FILE: tests/apps/socialaccount/providers/paypal/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/paypal/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.paypal.provider import PaypalProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class PaypalTests(OAuth2TestsMixin, TestCase): provider_id = PaypalProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "user_id": "https://www.paypal.com/webapps/auth/server/64ghr894040044", "name": "Jane Doe", "given_name": "Jane", "family_name": "Doe", "email": "janedoe@example.com" } """, ) def get_expected_to_str(self): return "janedoe@example.com" ================================================ FILE: tests/apps/socialaccount/providers/pinterest/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/pinterest/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from django.test.utils import override_settings from allauth.socialaccount.providers.pinterest.provider import PinterestProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class PinterestTests(OAuth2TestsMixin, TestCase): provider_id = PinterestProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "data": { "url": "https://www.pinterest.com/muravskiyyarosl/", "first_name": "Jane", "last_name": "Doe", "id": "351247977031674143" } } """, ) def get_expected_to_str(self): return "Jane Doe" @override_settings( SOCIALACCOUNT_AUTO_SIGNUP=False, SOCIALACCOUNT_PROVIDERS={ "pinterest": { "API_VERSION": "v5", } }, ) def test_login_v5(self): self.provider_id = PinterestProvider.id resp = self.login( MockedResponse( HTTPStatus.OK, """ { "account_type": "BUSINESS", "profile_image": "https://i.pinimg.com/280x280_RS/5c/88/2f/5c882f4b02468fcd6cda2ce569c2c166.jpg", "website_url": "https://sns-sdks.github.io/", "username": "enjoylifebot" } """, ), ) assert resp.status_code == HTTPStatus.FOUND ================================================ FILE: tests/apps/socialaccount/providers/pocket/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/pocket/tests.py ================================================ from http import HTTPStatus from urllib.parse import parse_qs, urlencode, urlparse from django.test import TestCase from django.urls import reverse from allauth.socialaccount.providers.pocket.provider import PocketProvider from tests.apps.socialaccount.base import OAuthTestsMixin from tests.mocking import MockedResponse, mocked_response class PocketOAuthTests(OAuthTestsMixin, TestCase): provider_id = PocketProvider.id def get_mocked_response(self): return [] def get_expected_to_str(self): return "name@example.com" def get_access_token_response(self): return MockedResponse( HTTPStatus.OK, """ {"access_token":"5678defg-5678-defg-5678-defg56", "username":"name@example.com"} """, ) def login(self, resp_mocks, process="login"): with mocked_response( MockedResponse( HTTPStatus.OK, """ {"code": "dcba4321-dcba-4321-dcba-4321dc"} """, {"content-type": "application/json"}, ) ): resp = self.client.post( reverse(f"{self.provider.id}_login") + "?" + urlencode(dict(process=process)) ) p = urlparse(resp["location"]) q = parse_qs(p.query) complete_url = reverse(f"{self.provider.id}_callback") self.assertGreater(q["redirect_uri"][0].find(complete_url), 0) with mocked_response(self.get_access_token_response(), *resp_mocks): resp = self.client.get(complete_url) return resp ================================================ FILE: tests/apps/socialaccount/providers/questrade/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/questrade/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.questrade.provider import QuestradeProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class QuestradeTests(OAuth2TestsMixin, TestCase): provider_id = QuestradeProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{"userId":400,"accounts":[]}""", ) def get_expected_to_str(self): return "Questrade" ================================================ FILE: tests/apps/socialaccount/providers/quickbooks/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/quickbooks/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.quickbooks.provider import QuickBooksOAuth2Provider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class QuickBooksOAuth2Tests(OAuth2TestsMixin, TestCase): provider_id = QuickBooksOAuth2Provider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "sub": "d8752092-0f2b-4b6e-86ef-6b72f2457a00", "emailVerified": true, "familyName": "Mckeeman", "phoneNumber": "+1 4156694355", "givenName": "Darren", "phoneNumberVerified": true, "email": "darren@blocklight.io"} """, ) def get_expected_to_str(self): return "darren@blocklight.io" ================================================ FILE: tests/apps/socialaccount/providers/reddit/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/reddit/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.reddit.provider import RedditProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class RedditTests(OAuth2TestsMixin, TestCase): provider_id = RedditProvider.id def get_mocked_response(self): return [ MockedResponse( HTTPStatus.OK, """{ "name": "wayward710"}""", ) ] def get_expected_to_str(self): return "wayward710" ================================================ FILE: tests/apps/socialaccount/providers/robinhood/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/robinhood/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.robinhood.provider import RobinhoodProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class RobinhoodTests(OAuth2TestsMixin, TestCase): provider_id = RobinhoodProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "username": "test_username", "id": "1234-5678-910" } """, ) def get_expected_to_str(self): return "test_username" ================================================ FILE: tests/apps/socialaccount/providers/salesforce/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/salesforce/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.salesforce.provider import SalesforceProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class SalesforceTests(OAuth2TestsMixin, TestCase): provider_id = SalesforceProvider.id def get_mocked_response( self, last_name="Penners", first_name="Raymond", name="Raymond Penners", email="raymond.penners@gmail.com", verified_email=True, ): userinfo = USERINFO_RESPONSE.format( org_id="00Dxx00000000000A0", user_id="005xx000000aWwRQAU", vip="https://test.salesforce.com", nickname="test-ooi2xhmjteep", first_name=first_name, last_name=last_name, my_domain="https://fun.cs46.my.salesforce.com", content_domain="https://fun--c.cs46.content.force.com", verified_email=repr(verified_email).lower(), email=email, active="true", is_app_installed="true", ) return MockedResponse(HTTPStatus.OK, userinfo) def get_expected_to_str(self): return "raymond.penners@gmail.com" USERINFO_RESPONSE = """ {{ "sub": "{vip}/id/{org_id}/{user_id}", "user_id": "{user_id}", "organization_id": "{org_id}", "preferred_username": "{nickname}@sample_-_dev_workspace.net", "nickname": "{nickname}", "name": "{first_name} {last_name}", "email": "{email}", "email_verified": {verified_email}, "given_name": "{first_name}", "family_name": "{last_name}", "zoneinfo": "America/Los_Angeles", "photos": {{ "picture": "{content_domain}/profilephoto/005/F", "thumbnail": "{content_domain}/profilephoto/005/T" }}, "profile": "{my_domain}/{user_id}", "picture": "{content_domain}/profilephoto/005/F", "address": {{"country": "US"}}, "urls": {{"custom_domain": "{my_domain}"}}, "active": {active}, "user_type": "STANDARD", "language": "en_US", "locale": "en_US", "utcOffset": -28800000, "updated_at": "2017-10-05T20:39:02.000+0000", "is_app_installed": {is_app_installed} }} """ ================================================ FILE: tests/apps/socialaccount/providers/saml/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/saml/conftest.py ================================================ import base64 from unittest.mock import patch from django.test.client import Client import pytest @pytest.fixture def client(): client = Client(HTTP_HOST="example.com") return client @pytest.fixture def saml_settings(settings): settings.SOCIALACCOUNT_PROVIDERS = { "saml": { "APPS": [ { "client_id": "org", "provider_id": "urn:dev-123.us.auth0.com", "settings": { "attribute_mapping": { "uid": "http://schemas.auth0.com/clientID", "email_verified": "http://schemas.auth0.com/email_verified", "email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", }, "idp": { "name": "Test IdP", "entity_id": "urn:dev-123.us.auth0.com", "sso_url": "https://dev-123.us.auth0.com/samlp/456", "slo_url": "https://dev-123.us.auth0.com/samlp/456", "x509cert": "", }, "advanced": { "strict": False, }, }, } ] } } @pytest.fixture def acs_saml_response_factory(): def factory(in_response_to=None): xml = f""" urn:dev-123.us.auth0.com urn:dev-123.us.auth0.com 123 If7dFg... MIIDHTCC... google-oauth2|108204123456789 https://allauth.org/accounts/org/metadata/ urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified google-oauth2|108204123456789 john.doe@email.org John John john.doe@email.org google-oauth2 google-oauth2 true dummysamluid Wed Jun 28 2023 17:53:49 GMT+0000 (Coordinated Universal Time) true en john.doe https://lh3.googleusercontent.com/a/AAcHTtfZ0fEyL3BKP1Hk2v1bNwpJd6ckIeo6jSExlkVjMXaIpsY=s96-c Sat Jul 08 2023 06:13:07 GMT+0000 (Coordinated Universal Time) view-profile manage-account-links """ return base64.b64encode(xml.encode("utf8")).decode("utf8") return factory @pytest.fixture def sls_saml_request(): xml = "" return base64.b64encode(xml.encode("utf8")).decode("utf8") @pytest.fixture def mocked_signature_validation(): with patch("onelogin.saml2.utils.OneLogin_Saml2_Utils.validate_sign") as mock: mock.return_value = True yield ================================================ FILE: tests/apps/socialaccount/providers/saml/tests.py ================================================ from http import HTTPStatus from unittest.mock import Mock, patch from urllib.parse import parse_qs, urlparse from django.conf import settings from django.urls import reverse, reverse_lazy from django.utils.http import urlencode import pytest from pytest_django.asserts import assertTemplateUsed from allauth.account.models import EmailAddress from allauth.socialaccount.adapter import get_adapter from allauth.socialaccount.internal import statekit from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.base.constants import AuthProcess from allauth.socialaccount.providers.saml.utils import build_saml_config @pytest.mark.parametrize( "idp_initiated,adv_settings,state_kwargs,relay_state, expected_url", [ (False, {}, {}, "/not/here", settings.LOGIN_REDIRECT_URL), (False, {}, {"next": "/here"}, "/not/here", "/here"), ( False, {}, {"process": "connect"}, "/not/here", reverse_lazy("socialaccount_connections"), ), (False, {}, {"process": "connect", "next": "/here"}, "/not/here", "/here"), (True, {"reject_idp_initiated_sso": False}, {}, "/set-by-idp", "/set-by-idp"), ( True, {"reject_idp_initiated_sso": False}, {}, "not-a-url", settings.LOGIN_REDIRECT_URL, ), (True, {}, {}, "/set-by-idp", "/set-by-idp"), ], ) def test_acs( request, idp_initiated, db, saml_settings, acs_saml_response_factory, mocked_signature_validation, expected_url, relay_state, state_kwargs, sociallogin_setup_state, adv_settings, settings, ): provider_settings = settings.SOCIALACCOUNT_PROVIDERS["saml"]["APPS"][0]["settings"] advanced = dict(provider_settings["advanced"]) advanced.update(adv_settings) provider_settings["advanced"] = advanced process = state_kwargs.setdefault("process", AuthProcess.LOGIN) is_connect = process == AuthProcess.CONNECT if is_connect: client = request.getfixturevalue("auth_client") user = request.getfixturevalue("user") else: client = request.getfixturevalue("client") user = None state_id = None if not idp_initiated: state_id = sociallogin_setup_state(client, **state_kwargs) data = {"SAMLResponse": acs_saml_response_factory(in_response_to=state_id)} if relay_state is not None: data["RelayState"] = relay_state resp = client.post( reverse("saml_acs", kwargs={"organization_slug": "org"}), data=data ) finish_url = reverse("saml_finish_acs", kwargs={"organization_slug": "org"}) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == finish_url resp = client.get(finish_url) if idp_initiated and advanced.get("reject_idp_initiated_sso", True): assert "socialaccount/authentication_error.html" in ( t.name for t in resp.templates ) else: assert resp["location"] == expected_url account = SocialAccount.objects.get( provider="urn:dev-123.us.auth0.com", uid="dummysamluid" ) assert account.extra_data["Role"] == ["view-profile", "manage-account-links"] email = EmailAddress.objects.get(user=account.user) assert email.email == (user.email if is_connect else "john.doe@email.org") def test_acs_error(client, db, saml_settings): data = {"SAMLResponse": "bad-response"} resp = client.post( reverse("saml_acs", kwargs={"organization_slug": "org"}), data=data ) assert resp.status_code == HTTPStatus.FOUND resp = client.get(resp["location"]) assert "socialaccount/authentication_error.html" in (t.name for t in resp.templates) def test_acs_get(client, db, saml_settings): """WHile ACS expects POST, it always redirects and handles the request in the FinishACSView. """ resp = client.get(reverse("saml_acs", kwargs={"organization_slug": "org"})) assert resp.status_code == HTTPStatus.FOUND resp = client.get(resp["location"]) assert "socialaccount/authentication_error.html" in (t.name for t in resp.templates) def test_sls_get(client, db, saml_settings): """SLS expects POST""" resp = client.get(reverse("saml_sls", kwargs={"organization_slug": "org"})) assert resp.status_code == HTTPStatus.BAD_REQUEST def test_login_on_get(client, db, saml_settings): resp = client.get(reverse("saml_login", kwargs={"organization_slug": "org"})) assert resp.status_code == HTTPStatus.OK assertTemplateUsed(resp, "socialaccount/login.html") def test_login(client, db, saml_settings): resp = client.post( reverse("saml_login", kwargs={"organization_slug": "org"}) + "?process=connect&next=/foo" ) assert resp.status_code == HTTPStatus.FOUND location = resp["location"] assert location.startswith("https://dev-123.us.auth0.com/samlp/456?SAMLRequest=") resp_query = parse_qs(urlparse(location).query) # We're not using RelayState assert resp_query.get("RelayState") is None # We're using the request ID / InResponseTo for tracking state. state_id = list(client.session[statekit.STATES_SESSION_KEY].keys())[0] assert state_id.startswith("ONELOGIN_") state = client.session[statekit.STATES_SESSION_KEY][state_id][0] assert state == {"process": "connect", "data": None, "next": "/foo"} def test_metadata( client, db, saml_settings, ): resp = client.get(reverse("saml_metadata", kwargs={"organization_slug": "org"})) assert resp.status_code == HTTPStatus.OK assert resp.content.startswith( b'\n" in actual_content and "" in actual_content, f"Expected standard HTML skeleton. [Actual: {actual_content}]", ) p = urlparse( actual_content.split(";")[0].split('location.href = "')[1] ) q = parse_qs(p.query) resp = self._complete_shopify_login(q, resp, resp_mock, with_refresh_token) return resp @override_settings( SOCIALACCOUNT_PROVIDERS={ "shopify": {"AUTH_PARAMS": {"grant_options[]": "per-user"}} } ) class ShopifyPerUserAccessTests(ShopifyTests): """ Shopify has two access modes, offline (the default) and online/per-user. Enabling 'online' access should cause all-auth to tie the logged in Shopify user to the all-auth account (rather than the shop as a whole). See Also: https://help.shopify.com/api/getting-started/authentication/ oauth#api-access-modes """ def get_login_response_json(self, with_refresh_token=True): response_data = { "access_token": "testac", "account_number": None, "associated_user": { "account_owner": True, "collaborator": False, "email": "john@example.com", "email_verified": True, "first_name": "John", "id": 902541635, "last_name": "Smith", "locale": "en", }, "associated_user_scope": "read_products,read_customers,read_inventory,write_merchant_managed_fulfillment_orders,write_third_party_fulfillment_orders,read_orders,write_assigned_fulfillment_orders", "expires_in": 86381, "scope": "read_products,read_customers,read_inventory,write_merchant_managed_fulfillment_orders,write_third_party_fulfillment_orders,read_orders,write_assigned_fulfillment_orders", "session": None, } if with_refresh_token: response_data["refresh_token"] = "testrf" return json.dumps(response_data) @override_settings( SOCIALACCOUNT_AUTO_SIGNUP=True, SOCIALACCOUNT_EMAIL_REQUIRED=True, ACCOUNT_EMAIL_REQUIRED=True, ) def test_associated_user(self): resp_mocks = self.get_mocked_response() resp = self.login(resp_mocks) self.assertRedirects(resp, "/accounts/profile/", fetch_redirect_response=False) social_account = SocialAccount.objects.filter( provider=self.provider.id, uid=902541635, ).first() self.assertIsNotNone(social_account) self.assertTrue("associated_user" in social_account.extra_data) self.assertEqual(social_account.user.email, "john@example.com") self.assertEqual(social_account.user.first_name, "John") self.assertEqual(social_account.user.last_name, "Smith") ================================================ FILE: tests/apps/socialaccount/providers/slack/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/slack/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.slack.provider import SlackProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class SlackOAuth2Tests(OAuth2TestsMixin, TestCase): provider_id = SlackProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "ok": true, "sub": "U0R7JM", "https://slack.com/user_id": "U0R7JM", "https://slack.com/team_id": "T0R7GR", "email": "krane@slack-corp.com", "email_verified": true, "date_email_verified": 1622128723, "name": "krane", "picture": "https://secure.gravatar.com/....png", "given_name": "Bront", "family_name": "Labradoodle", "locale": "en-US", "https://slack.com/team_name": "kraneflannel", "https://slack.com/team_domain": "kraneflannel" }""", ) # noqa def get_expected_to_str(self): return "krane@slack-corp.com" ================================================ FILE: tests/apps/socialaccount/providers/snapchat/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/snapchat/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.snapchat.provider import SnapchatProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class SnapchatOAuth2Tests(OAuth2TestsMixin, TestCase): provider_id = SnapchatProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "data":{ "me":{ "externalId":"CAESIPiRBp0e5gLDq7VVurQ3rVdmdbqxpOJWynjyBL/xlo0w", "displayName":"Karun Shrestha", "bitmoji":{ "avatar":"https://sdk.bitmoji.com/render/panel/336d1e96-9055-4818-81aa-adde45ec030f-3aBXH5B0ZPCr~grPTZScjprXRT2RkU90oSd7X_PjDFFnBe3wuFkD1R-v1.png?transparent=1&palette=1", "id":"3aBXH5B0ZPCr~grPTZScjprXRT2RkU90oSd7X_PjDFFnBe3wuFkD1R" } } }, "errors":[] }""", ) # noqa def get_expected_to_str(self): return "Karun Shrestha" ================================================ FILE: tests/apps/socialaccount/providers/soundcloud/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/soundcloud/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.soundcloud.provider import SoundCloudProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class SoundCloudTests(OAuth2TestsMixin, TestCase): provider_id = SoundCloudProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "website": null, "myspace_name": null, "public_favorites_count": 0, "followings_count": 1, "full_name": "", "urn": "soundcloud:users:22341947", "city": null, "track_count": 0, "playlist_count": 0, "discogs_name": null, "private_tracks_count": 0, "followers_count": 0, "online": true, "username": "user187631676", "description": null, "kind": "user", "website_title": null, "primary_email_confirmed": false, "permalink_url": "http://soundcloud.com/user187631676", "private_playlists_count": 0, "permalink": "user187631676", "country": null, "uri": "https://api.soundcloud.com/users/22341947", "avatar_url": "https://a1.sndcdn.com/images/default_avatar_large.png?4b4189b", "plan": "Free" }""", ) def get_expected_to_str(self): return "user187631676" ================================================ FILE: tests/apps/socialaccount/providers/spotify/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/spotify/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.spotify.provider import SpotifyOAuth2Provider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class SpotifyOAuth2Tests(OAuth2TestsMixin, TestCase): provider_id = SpotifyOAuth2Provider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "birthdate": "1937-06-01", "country": "SE", "display_name": "JM Wizzler", "email": "email@example.com", "external_urls": { "spotify": "https://open.spotify.com/user/wizzler" }, "followers" : { "href" : null, "total" : 3829 }, "href": "https://api.spotify.com/v1/users/wizzler", "id": "wizzler", "images": [ { "height": null, "url": "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-frc3/t1.0-1/1970403_10152215092574354_1798272330_n.jpg", "width": null } ], "product": "premium", "type": "user", "uri": "spotify:user:wizzler" }""", ) # noqa def get_expected_to_str(self): return "email@example.com" ================================================ FILE: tests/apps/socialaccount/providers/stackexchange/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/stackexchange/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.stackexchange.provider import StackExchangeProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class StackExchangeTests(OAuth2TestsMixin, TestCase): provider_id = StackExchangeProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "has_more": false, "items": [ { "is_employee": false, "last_access_date": 1356200390, "display_name": "pennersr", "account_id": 291652, "badge_counts": { "bronze": 2, "silver": 2, "gold": 0 }, "last_modified_date": 1356199552, "profile_image": "http://www.gravatar.com/avatar/053d648486d567d3143d6bad8df8cfeb?d=identicon&r=PG", "user_type": "registered", "creation_date": 1296223711, "reputation_change_quarter": 148, "reputation_change_year": 378, "reputation": 504, "link": "http://stackoverflow.com/users/593944/pennersr", "reputation_change_week": 0, "user_id": 593944, "reputation_change_month": 10, "reputation_change_day": 0 } ], "quota_max": 10000, "quota_remaining": 9999 }""", ) # noqa def get_expected_to_str(self): return "pennersr" ================================================ FILE: tests/apps/socialaccount/providers/steam/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/steam/tests.py ================================================ from unittest.mock import Mock, patch from django.conf import settings from django.test import TestCase from django.urls import reverse from openid.yadis.discover import DiscoveryFailure from pytest_django.asserts import assertRedirects from allauth.socialaccount.providers.steam import views from allauth.socialaccount.providers.steam.provider import SteamOpenIDProvider from tests.apps.socialaccount.base import setup_app class SteamTests(TestCase): def setUp(self): self.app = setup_app(SteamOpenIDProvider.id) def test_redirect(self): with patch( "allauth.socialaccount.providers.steam.provider._openid_consumer" ) as consumer_mock: consumer_client = Mock() begin = Mock() auth_request = Mock() redirectURL = Mock() consumer_mock.return_value = consumer_client consumer_client.begin = begin begin.return_value = auth_request auth_request.redirectURL = redirectURL redirectURL.return_value = "https://steamcommunity.com/openid/login?XXX" resp = self.client.post(reverse(views.steam_login)) assertRedirects( resp, "https://steamcommunity.com/openid/login?XXX", fetch_redirect_response=False, ) def test_redirect_error(self): with patch( "allauth.socialaccount.providers.steam.provider._openid_consumer" ) as consumer_mock: consumer_client = Mock() begin = Mock() consumer_mock.return_value = consumer_client consumer_client.begin = begin def discovery_failure(*args, **kwargs): raise DiscoveryFailure( "HTTP Response status from identity URL host is not 200. Got status 502", None, ) begin.side_effect = discovery_failure resp = self.client.post(reverse(views.steam_login)) template_ext = getattr(settings, "ACCOUNT_TEMPLATE_EXTENSION", "html") self.assertTemplateUsed( resp, f"socialaccount/authentication_error.{template_ext}" ) ================================================ FILE: tests/apps/socialaccount/providers/stocktwits/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/stocktwits/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.stocktwits.provider import StocktwitsProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class StocktwitsTests(OAuth2TestsMixin, TestCase): provider_id = StocktwitsProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "response": { "status": 200 }, "user": { "id": 3, "username": "zerobeta", "name": "Justin Paterno", "avatar_url": "http://avatars.stocktwits.com/images/default_avatar_thumb.jpg", "avatar_url_ssl": "https://s3.amazonaws.com/st-avatars/images/default_avatar_thumb.jpg", "identity": "Official", "classification": [ "ir" ] } } """, ) # noqa def get_expected_to_str(self): return "zerobeta" ================================================ FILE: tests/apps/socialaccount/providers/strava/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/strava/tests.py ================================================ from http import HTTPStatus from django.contrib.auth.models import User from django.test import TestCase from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.strava.provider import StravaProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class StravaTests(OAuth2TestsMixin, TestCase): provider_id = StravaProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "id": 32641234, "username": null, "resource_state": 2, "firstname": "georges", "lastname": "camembert", "city": "London", "state": "England", "country": "United Kingdom", "sex": "M", "premium": false, "summit": false, "created_at": "2017-07-12T12:42:52Z", "updated_at": "2017-10-21T11:01:23Z", "badge_type_id": 0, "profile_medium": "avatar/athlete/medium.png", "profile": "avatar/athlete/large.png", "friend": null, "follower": null, "email": "bill@example.com" }""", ) # noqa def get_expected_to_str(self): return "bill@example.com" def get_mocked_response_avatar_invalid_id(self): """Profile including realistic avatar URL user ID set to 0 to test edge case where id would be missing""" return MockedResponse( HTTPStatus.OK, """{ "id": 0, "username": null, "resource_state": 2, "firstname": "georges", "lastname": "camembert", "city": "London", "state": "England", "country": "United Kingdom", "sex": "M", "premium": false, "summit": false, "created_at": "2017-07-12T12:42:52Z", "updated_at": "2017-10-21T11:01:23Z", "badge_type_id": 0, "profile_medium": "https://cloudfront.net/1/medium.jpg", "profile": "https://cloudfront.net/1/large.jpg", "friend": null, "follower": null, "email": "bill@example.com" }""", ) # noqa def test_valid_avatar(self): """test response with Avatar URL""" self.login(self.get_mocked_response_avatar_invalid_id()) user = User.objects.get(email="bill@example.com") soc_acc = SocialAccount.objects.filter( user=user, provider=self.provider.id ).get() provider_account = soc_acc.get_provider_account() self.assertEqual( provider_account.get_avatar_url(), "https://cloudfront.net/1/large.jpg", ) self.assertIsNone(provider_account.get_profile_url()) def get_login_response_json(self, with_refresh_token=True): rt = "" if with_refresh_token: rt = ',"refresh_token": "testrf"' return ( """{ "uid":"weibo", "access_token":"testac", "livemode": false, "token_type": "bearer", "strava_publishable_key": "pk_test_someteskey", "strava_user_id": "acct_sometestid", "scope": "read_write" %s }""" % rt ) ================================================ FILE: tests/apps/socialaccount/providers/stripe/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/stripe/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.stripe.provider import StripeProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class StripeTests(OAuth2TestsMixin, TestCase): provider_id = StripeProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "id": "acct_sometestid", "object": "account", "business_logo": null, "business_name": null, "business_url": "example.com", "charges_enabled": true, "country": "SE", "currencies_supported": [ "usd", "eur", "sek" ], "default_currency": "eur", "details_submitted": true, "display_name": "Test", "email": "test@example.com", "managed": false, "metadata": {}, "statement_descriptor": "TEST.COM", "support_phone": "+460123456789", "timezone": "Europe/Stockholm", "transfers_enabled": true }""", ) def get_expected_to_str(self): return "test@example.com" def get_login_response_json(self, with_refresh_token=True): rt = "" if with_refresh_token: rt = ',"refresh_token": "testrf"' return ( """{ "uid":"weibo", "access_token":"testac", "livemode": false, "token_type": "bearer", "stripe_publishable_key": "pk_test_someteskey", "stripe_user_id": "acct_sometestid", "scope": "read_write" %s }""" % rt ) ================================================ FILE: tests/apps/socialaccount/providers/telegram/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/telegram/tests.py ================================================ import base64 import json from http import HTTPStatus from django.urls import reverse import pytest from pytest_django.asserts import assertTemplateUsed from allauth.socialaccount.models import SocialAccount @pytest.fixture def telegram_app(settings): settings.SOCIALACCOUNT_PROVIDERS = { "telegram": { "APPS": [ { "client_id": "123", } ] } } def test_login(client, db, telegram_app): resp = client.post(reverse("telegram_login")) assert resp.status_code == HTTPStatus.FOUND assert resp["location"].startswith( "https://oauth.telegram.org/auth?origin=http%3A%2F%2Ftestserver%2F&bot_id=123&request_access=write&embed=0&return_to=http%3A%2F%2Ftestserver%2Faccounts%2Ftelegram%2Flogin%2Fcallback%2F%3Fstate%3D" ) def test_callback_get(client, db, telegram_app): resp = client.get(reverse("telegram_callback")) assert resp.status_code == HTTPStatus.OK assertTemplateUsed(resp, "telegram/callback.html") def test_callback(client, db, telegram_app, sociallogin_setup_state): state = sociallogin_setup_state(client) auth_result = ( base64.b64encode( json.dumps( { "id": "123", "hash": "0744ab643757850e82fa8b4ac35978dca287c81df6a9829032d868c7f90e3b99", "auth_date": 2342342342, } ).encode("utf8") ) .decode("ascii") .replace("=", "") ) post_data = { "tgAuthResult": auth_result, } resp = client.post(f"{reverse('telegram_callback')}?state={state}", post_data) assert resp.status_code == HTTPStatus.FOUND assert SocialAccount.objects.filter(uid="123").exists() ================================================ FILE: tests/apps/socialaccount/providers/tiktok/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/tiktok/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.tiktok.provider import TikTokProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class TikTokTests(OAuth2TestsMixin, TestCase): provider_id = TikTokProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "data": { "user": { "open_id": "44322889", "username": "username123", "display_name": "Nice Display Name", "avatar_url": "https://example.com/avatar.jpg", "profile_deep_link": "https://example.com/profile" } } } """, ) def get_expected_to_str(self): return "username123" ================================================ FILE: tests/apps/socialaccount/providers/trainingpeaks/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/trainingpeaks/tests.py ================================================ """ Run just this suite: python manage.py test allauth.socialaccount.providers.trainingpeaks.tests.TrainingPeaksTests """ from collections import namedtuple from http import HTTPStatus from django.test import TestCase from django.test.utils import override_settings from allauth.socialaccount.providers.trainingpeaks.provider import TrainingPeaksProvider from allauth.socialaccount.providers.trainingpeaks.views import ( TrainingPeaksOAuth2Adapter, ) from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class TrainingPeaksTests(OAuth2TestsMixin, TestCase): provider_id = TrainingPeaksProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "Id": 123456, "FirstName": "John", "LastName": "Doe", "Email": "user@example.com", "DateOfBirth": "1986-02-01T00:00:00", "CoachedBy": 987654, "Weight": 87.5223617553711 }""", ) # noqa def get_expected_to_str(self): return "user@example.com" def get_login_response_json(self, with_refresh_token=True): rtoken = "" if with_refresh_token: rtoken = ',"refresh_token": "testrf"' return ( """{ "access_token" : "testac", "token_type" : "bearer", "expires_in" : 600, "scope": "scopes granted" %s }""" % rtoken ) def test_default_use_sandbox_uri(self): adapter = TrainingPeaksOAuth2Adapter(None) self.assertTrue(".sandbox." in adapter.authorize_url) self.assertTrue(".sandbox." in adapter.access_token_url) self.assertTrue(".sandbox." in adapter.profile_url) @override_settings( SOCIALACCOUNT_PROVIDERS={"trainingpeaks": {"USE_PRODUCTION": True}} ) def test_use_production_uri(self): adapter = TrainingPeaksOAuth2Adapter(None) self.assertFalse(".sandbox." in adapter.authorize_url) self.assertFalse(".sandbox." in adapter.access_token_url) self.assertFalse(".sandbox." in adapter.profile_url) def test_scope_from_default(self): Request = namedtuple("request", ["GET"]) mock_request = Request(GET={}) scope = self.provider.get_scope_from_request(mock_request) self.assertTrue("athlete:profile" in scope) @override_settings( SOCIALACCOUNT_PROVIDERS={ "trainingpeaks": {"SCOPE": ["athlete:profile", "workouts", "workouts:wod"]} } ) def test_scope_from_settings(self): Request = namedtuple("request", ["GET"]) mock_request = Request(GET={}) scope = self.provider.get_scope_from_request(mock_request) for item in ("athlete:profile", "workouts", "workouts:wod"): self.assertTrue(item in scope) ================================================ FILE: tests/apps/socialaccount/providers/trello/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/trello/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.trello.provider import TrelloProvider from tests.apps.socialaccount.base import OAuthTestsMixin from tests.mocking import MockedResponse class TrelloTests(OAuthTestsMixin, TestCase): provider_id = TrelloProvider.id def get_mocked_response(self): return [ MockedResponse( HTTPStatus.OK, r""" {"id": "123", "email": "raymond.penners@example.com", "username": "pennersr", "name": "Raymond"} """, ), ] # noqa def get_expected_to_str(self): return "pennersr" ================================================ FILE: tests/apps/socialaccount/providers/tumblr/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/tumblr/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.tumblr.provider import TumblrProvider from tests.apps.socialaccount.base import OAuthTestsMixin from tests.mocking import MockedResponse class TumblrTests(OAuthTestsMixin, TestCase): provider_id = TumblrProvider.id def get_mocked_response(self): return [ MockedResponse( HTTPStatus.OK, """ { "meta": { "status": 200, "msg": "OK" }, "response": { "user": { "following": 263, "default_post_format": "html", "name": "derekg", "likes": 606, "blogs": [ { "name": "derekg", "title": "Derek Gottfrid", "url": "http://derekg.org/", "tweet": "auto", "primary": true, "followers": 33004929 }, { "name": "ihatehipstrz", "title": "I Hate Hipstrz" } ] } } } """, ) ] def get_expected_to_str(self): return "derekg" ================================================ FILE: tests/apps/socialaccount/providers/tumblr_oauth2/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/tumblr_oauth2/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.tumblr_oauth2.provider import TumblrOAuth2Provider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class TumblrTests(OAuth2TestsMixin, TestCase): provider_id = TumblrOAuth2Provider.id def get_mocked_response(self): return [ MockedResponse( HTTPStatus.OK, """ { "meta": { "status": 200, "msg": "OK" }, "response": { "user": { "following": 263, "default_post_format": "html", "name": "derekg", "likes": 606, "blogs": [ { "name": "derekg", "title": "Derek Gottfrid", "url": "http://derekg.org/", "tweet": "auto", "primary": true, "followers": 33004929 }, { "name": "ihatehipstrz", "title": "I Hate Hipstrz" } ] } } } """, ) ] def get_expected_to_str(self): return "derekg" ================================================ FILE: tests/apps/socialaccount/providers/twentythreeandme/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/twentythreeandme/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.twentythreeandme.provider import ( TwentyThreeAndMeProvider, ) from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class TwentyThreeAndMeTests(OAuth2TestsMixin, TestCase): provider_id = TwentyThreeAndMeProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "profiles": [ {"id": "56c46bdb0902f8e2", "genotyped": false} ], "id": "b4b975a5a6a1b80b" } """, ) def get_expected_to_str(self): return "23andMe" def get_login_response_json(self, with_refresh_token=True): return """ { "access_token":"testac", "token_type":"bearer", "expires_in": 86400, "refresh_token":"33c53cd7bb", "scope":"basic" }""" ================================================ FILE: tests/apps/socialaccount/providers/twitch/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/twitch/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from django.test.client import RequestFactory from django.urls import reverse from allauth.socialaccount.models import SocialToken from allauth.socialaccount.providers.oauth2.client import OAuth2Error from allauth.socialaccount.providers.twitch.provider import TwitchProvider from allauth.socialaccount.providers.twitch.views import TwitchOAuth2Adapter from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse, mocked_response class TwitchTests(OAuth2TestsMixin, TestCase): provider_id = TwitchProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "data": [{ "id": "44322889", "login": "dallas", "display_name": "dallas", "type": "staff", "broadcaster_type": "", "description": "Just a gamer playing games and chatting. :)", "profile_image_url": "https://static-cdn.jtvnw.net/jtv_user_pictures/dallas-profile_image-1a2c906ee2c35f12-300x300.png", "offline_image_url": "https://static-cdn.jtvnw.net/jtv_user_pictures/dallas-channel_offline_image-1a2c906ee2c35f12-1920x1080.png", "view_count": 191836881, "email": "login@provider.com" }] } """, ) # noqa def get_expected_to_str(self): return "dallas" def test_response_over_400_raises_OAuth2Error(self): resp_mock = MockedResponse(HTTPStatus.BAD_REQUEST, '{"error": "Invalid token"}') expected_error = "Twitch API Error: Invalid token ()" self.check_for_error(resp_mock, expected_error) def test_empty_or_missing_data_key_raises_OAuth2Error(self): resp_mock = MockedResponse(HTTPStatus.OK, '{"data": []}') expected_error = "Invalid data from Twitch API: {'data': []}" self.check_for_error(resp_mock, expected_error) resp_mock = MockedResponse(HTTPStatus.OK, '{"missing_data": "key"}') expected_error = "Invalid data from Twitch API: {'missing_data': 'key'}" self.check_for_error(resp_mock, expected_error) def test_missing_twitch_id_raises_OAuth2Error(self): resp_mock = MockedResponse( HTTPStatus.OK, '{"data": [{"login": "fake_twitch"}]}' ) expected_error = "Invalid data from Twitch API: {'login': 'fake_twitch'}" self.check_for_error(resp_mock, expected_error) def check_for_error(self, resp_mock, expected_error): with self.assertRaises(OAuth2Error) as error_ctx: self._run_just_complete_login(resp_mock) self.assertEqual(str(error_ctx.exception).replace("u", ""), expected_error) def _run_just_complete_login(self, resp_mock): """ Helper function for checking that Error cases are handled correctly. Running only `complete_login` means we can check that the specific errors are raised before they are caught and rendered to generic error HTML """ request = RequestFactory().get( reverse(f"{self.provider.id}_login"), {"process": "login"}, ) adapter = TwitchOAuth2Adapter(request) app = adapter.get_provider().app token = SocialToken(token="this-is-my-fake-token") with mocked_response(resp_mock): adapter = TwitchOAuth2Adapter(request) adapter.complete_login(request, app, token) ================================================ FILE: tests/apps/socialaccount/providers/twitter/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/twitter/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.twitter.provider import TwitterProvider from tests.apps.socialaccount.base import OAuthTestsMixin from tests.mocking import MockedResponse class TwitterTests(OAuthTestsMixin, TestCase): provider_id = TwitterProvider.id def get_mocked_response(self): # TODO: Replace with actual/complete Twitter response return [ MockedResponse( HTTPStatus.OK, r""" {"follow_request_sent": false, "profile_use_background_image": true, "id": 45671919, "verified": false, "profile_text_color": "333333", "profile_image_url_https": "https://pbs.twimg.com/profile_images/793142149/r_normal.png", "profile_sidebar_fill_color": "DDEEF6", "is_translator": false, "geo_enabled": false, "entities": {"description": {"urls": []}}, "followers_count": 43, "protected": false, "location": "The Netherlands", "default_profile_image": false, "id_str": "45671919", "status": {"contributors": null, "truncated": false, "text": "RT @denibertovic: Okay I'm definitely using django-allauth from now on. So easy to set up, far less time consuming, and it just works. #dja\u2026", "in_reply_to_status_id": null, "id": 400658301702381568, "favorite_count": 0, "source": "Twitter Web Client", "retweeted": true, "coordinates": null, "entities": {"symbols": [], "user_mentions": [{"indices": [3, 16], "screen_name": "denibertovic", "id": 23508244, "name": "Deni Bertovic", "id_str": "23508244"}], "hashtags": [{"indices": [135, 139], "text": "dja"}], "urls": []}, "in_reply_to_screen_name": null, "id_str": "400658301702381568", "retweet_count": 6, "in_reply_to_user_id": null, "favorited": false, "retweeted_status": {"lang": "en", "favorited": false, "in_reply_to_user_id": null, "contributors": null, "truncated": false, "text": "Okay I'm definitely using django-allauth from now on. So easy to set up, far less time consuming, and it just works. #django", "created_at": "Sun Jul 28 19:56:26 +0000 2013", "retweeted": true, "in_reply_to_status_id": null, "coordinates": null, "id": 361575897674956800, "entities": {"symbols": [], "user_mentions": [], "hashtags": [{"indices": [117, 124], "text": "django"}], "urls": []}, "in_reply_to_status_id_str": null, "in_reply_to_screen_name": null, "source": "web", "place": null, "retweet_count": 6, "geo": null, "in_reply_to_user_id_str": null, "favorite_count": 8, "id_str": "361575897674956800"}, "geo": null, "in_reply_to_user_id_str": null, "lang": "en", "created_at": "Wed Nov 13 16:15:57 +0000 2013", "in_reply_to_status_id_str": null, "place": null}, "utc_offset": 3600, "statuses_count": 39, "description": "", "friends_count": 83, "profile_link_color": "0084B4", "profile_image_url": "http://pbs.twimg.com/profile_images/793142149/r_normal.png", "notifications": false, "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", "profile_background_color": "C0DEED", "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", "name": "Raymond Penners", "lang": "nl", "profile_background_tile": false, "favourites_count": 0, "screen_name": "pennersr", "url": null, "created_at": "Mon Jun 08 21:10:45 +0000 2009", "contributors_enabled": false, "time_zone": "Amsterdam", "profile_sidebar_border_color": "C0DEED", "default_profile": true, "following": false, "listed_count": 1} """, ) ] # noqa def get_expected_to_str(self): return "pennersr" def test_login(self): super().test_login() account = SocialAccount.objects.get(uid="45671919") tw_account = account.get_provider_account() self.assertEqual(tw_account.get_screen_name(), "pennersr") self.assertEqual( tw_account.get_avatar_url(), "http://pbs.twimg.com/profile_images/793142149/r.png", ) self.assertEqual(tw_account.get_profile_url(), "https://x.com/pennersr") self.assertEqual(tw_account.to_str(), "pennersr") ================================================ FILE: tests/apps/socialaccount/providers/twitter_oauth2/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/twitter_oauth2/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.twitter_oauth2.provider import ( TwitterOAuth2Provider, ) from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class TwitterOAuth2Tests(OAuth2TestsMixin, TestCase): provider_id = TwitterOAuth2Provider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "data": { "created_at": "2020-09-02T13:39:14.000Z", "id": "1301152652357595137", "verified": false, "username": "realllkk520", "name": "realllkk520", "profile_image_url": "https://pbs.twimg.com/profile_images/1537259565632593920/OoRGPbUg_normal.jpg" } } """, ) # noqa def get_expected_to_str(self): return "realllkk520" ================================================ FILE: tests/apps/socialaccount/providers/untappd/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/untappd/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.untappd.provider import UntappdProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class UntappdTests(OAuth2TestsMixin, TestCase): provider_id = UntappdProvider.id def get_login_response_json(self, with_refresh_token=True): return """ { "meta": { "http_code": 200 }, "response": { "access_token": "testac" } }""" def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "meta":{ "code":200, "response_time":{ "time":0.29, "measure":"seconds" }, "init_time":{ "time":0.011, "measure":"seconds" } }, "notifications":{ "type":"notifications", "unread_count":{ "comments":0, "toasts":0, "friends":0, "messages":0, "news":0 } }, "response":{ "user":{ "uid":123456, "id":123456, "user_name":"groovecoder", "first_name":"", "last_name":"", "user_avatar":"https:\\/\\/gravatar.com\\/avatar\\/ec25d046746de3be33779256f6957d8f?size=100&d=https%3A%2F%2Funtappd.akamaized.net%2Fsite%2Fassets%2Fimages%2Fdefault_avatar_v2.jpg%3Fv%3D1", "user_avatar_hd":"https:\\/\\/gravatar.com\\/avatar\\/ec25d046746de3be33779256f6957d8f?size=125&d=https%3A%2F%2Funtappd.akamaized.net%2Fsite%2Fassets%2Fimages%2Fdefault_avatar_v2.jpg%3Fv%3D1", "user_cover_photo":"https:\\/\\/untappd.akamaized.net\\/site\\/assets\\/v3\\/images\\/cover_default.jpg", "user_cover_photo_offset":0, "is_private":0, "location":"Testville", "url":"", "bio":"", "is_supporter":0, "relationship":"self", "untappd_url":"http:\\/\\/untappd.com\\/user\\/testuser", "account_type":"user", "stats":{ "total_badges":43, "total_friends":43, "total_checkins":73, "total_beers":61, "total_created_beers":1, "total_followings":9, "total_photos":31 }, "recent_brews":{}, "checkins":{}, "media":{}, "contact":{}, "date_joined":"Tue, 11 Dec 2012 14:27:53 +0000", "settings":{ "badge":{ "badges_to_facebook":1, "badges_to_twitter":1 }, "checkin":{ "checkin_to_facebook":0, "checkin_to_twitter":0, "checkin_to_foursquare":0 }, "navigation":{ "default_to_checkin":0 }, "email_address":"test@example.com" } } } } """, ) def get_expected_to_str(self): return "groovecoder" ================================================ FILE: tests/apps/socialaccount/providers/vimeo/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/vimeo/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.vimeo.provider import VimeoProvider from tests.apps.socialaccount.base import OAuthTestsMixin from tests.mocking import MockedResponse class VimeoTests(OAuthTestsMixin, TestCase): provider_id = VimeoProvider.id def get_mocked_response(self): return [ MockedResponse( HTTPStatus.OK, """ {"generated_in":"0.0137", "stat":"ok","person":{ "created_on": "2013-04-08 14:24:47", "id":"17574504", "is_contact":"0", "is_plus":"0","is_pro":"0","is_staff":"0","is_subscribed_to":"0", "username":"user17574504","display_name":"Raymond Penners","location":"", "url":[""],"bio":"","number_of_contacts":"0","number_of_uploads":"0", "number_of_likes":"0","number_of_videos":"0", "number_of_videos_appears_in":"0","number_of_albums":"0", "number_of_channels":"0","number_of_groups":"0", "profileurl":"http:\\/\\/vimeo.com\\/user17574504", "videosurl":"http:\\/\\/vimeo.com\\/user17574504\\/videos", "portraits":{"portrait":[{"height":"30","width":"30", "_content": "http:\\/\\/a.vimeocdn.com\\/images_v6\\/portraits\\/portrait_30_yellow.png"}, {"height":"75","width":"75","_content": "http:\\/\\/a.vimeocdn.com\\/images_v6\\/portraits\\/portrait_75_yellow.png"}, {"height":"100","width":"100","_content": "http:\\/\\/a.vimeocdn.com\\/images_v6\\/portraits\\/portrait_100_yellow.png"}, {"height":"300","width":"300","_content": "http:\\/\\/a.vimeocdn.com\\/images_v6\\/portraits\\/portrait_300_yellow.png"}]}}} """, ) ] def get_expected_to_str(self): return "user17574504" ================================================ FILE: tests/apps/socialaccount/providers/vimeo_oauth2/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/vimeo_oauth2/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.vimeo_oauth2.provider import VimeoOAuth2Provider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class VimeoOAuth2Tests(OAuth2TestsMixin, TestCase): provider_id = VimeoOAuth2Provider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """{ "uri": "/users/12345", "name": "AllAuth", "link": "https://vimeo.com/user12345", "created_time": "2012-06-04T00:02:16+00:00", "pictures": { "uri": null, "active": false, "type": "default", "sizes": [{ "width": 30, "height": 30, "link": "https://i.vimeocdn.com/portrait/defaults-blue_30x30.png" }], "resource_key": "1234567890abcdef" }, "resource_key": "1234567890abcdef", "account": "pro" }""", ) # noqa def get_expected_to_str(self): return "AllAuth" ================================================ FILE: tests/apps/socialaccount/providers/vk/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/vk/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.vk.provider import VKProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class VKTests(OAuth2TestsMixin, TestCase): provider_id = VKProvider.id def get_mocked_response(self, verified_email=True): return MockedResponse( HTTPStatus.OK, """ { "user": { "user_id": "1234567890", "first_name": "Ivan", "last_name": "I.", "phone": "79991234567", "avatar": "http://avatar.com/12345678", "email": "ivan_i123@vk.ru", "sex": 2, "verified": false, "birthday": "01.01.2000" } } """, ) def get_expected_to_str(self): return "ivan_i123@vk.ru" def get_login_response_json(self, with_refresh_token=True): return """ { "access_token": "testac", "refresh_token": "XXXXX", "expires_in": 0, "user_id": 1234567890, "state": "XXX", "scope": "email phone" } """ ================================================ FILE: tests/apps/socialaccount/providers/wahoo/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/wahoo/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.wahoo.provider import WahooProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class WahooTests(OAuth2TestsMixin, TestCase): provider_id = WahooProvider.id def get_mocked_response(self): # https://cloud-api.wahooligan.com/#users return MockedResponse( HTTPStatus.OK, """ { "id": 60462, "height": "2.0", "weight": "80.0", "first": "Bob", "last": "Smith", "email": "sample@test-domain.com", "birth": "1980-10-02", "gender": 1, "created_at": "2018-10-23T15:38:23.000Z", "updated_at": "2018-10-24T20:46:40.000Z" } """, ) def get_expected_to_str(self): return "sample@test-domain.com" ================================================ FILE: tests/apps/socialaccount/providers/weibo/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/weibo/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.weibo.provider import WeiboProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class WeiboTests(OAuth2TestsMixin, TestCase): provider_id = WeiboProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ {"bi_followers_count": 0, "domain": "", "avatar_large": "http://tp3.sinaimg.cn/3195025850/180/0/0", "block_word": 0, "star": 0, "id": 3195025850, "city": "1", "verified": false, "follow_me": false, "verified_reason": "", "followers_count": 6, "location": "\u5317\u4eac \u4e1c\u57ce\u533a", "mbtype": 0, "profile_url": "u/3195025850", "province": "11", "statuses_count": 0, "description": "", "friends_count": 0, "online_status": 0, "mbrank": 0, "idstr": "3195025850", "profile_image_url": "http://tp3.sinaimg.cn/3195025850/50/0/0", "allow_all_act_msg": false, "allow_all_comment": true, "geo_enabled": true, "name": "pennersr", "lang": "zh-cn", "weihao": "", "remark": "", "favourites_count": 0, "screen_name": "pennersr", "url": "", "gender": "f", "created_at": "Tue Feb 19 19:43:39 +0800 2013", "verified_type": -1, "following": false} """, ) def get_expected_to_str(self): return "pennersr" ================================================ FILE: tests/apps/socialaccount/providers/weixin/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/weixin/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.weixin.provider import WeixinProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class WeixinTests(OAuth2TestsMixin, TestCase): provider_id = WeixinProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ {"access_token": "OezXcEiiBSKSxW0eoylIeO5cPxb4Ks1RpbXGMv9uiV35032zNHGzXcld-EKsSScE3gRZMrUU78skCbp1ShtZnR0dQB8Wr_LUf7FA-H97Lnd2HgQah_GnkQex-vPFsGEwPPcNAV6q1Vz3uRNgL0MUFg", "city": "Pudong New District", "country": "CN", "expires_in": 7200, "headimgurl": "http://wx.qlogo.cn/mmopen/VkvLVEpoJiaibYsVyW8GzxHibzlnqSM7iaX09r6TWUJXCNQHibHz37krvN65HR1ibEpgH5K5sukcIzA3r1C4KQ9qyyX9XIUdY9lNOk/0", "language": "zh_CN", "nickname": "某某某", "openid": "ohS-VwAJ9GEXlplngwybJ3Z-ZHrI", "privilege": [], "province": "Shanghai", "refresh_token": "OezXcEiiBSKSxW0eoylIeO5cPxb4Ks1RpbXGMv9uiV35032zNHGzXcld-EKsSScEbMnnMqVExcSpj7KRAuBA8BU2j2e_FK5dgBe-ro32k7OuHtznwqqBn5QR7LZGo2-P8G7gG0eitjyZ751sFlnTAw", "scope": "snsapi_login", "sex": 1, "unionid": "ohHrhwKnD9TOunEW0eKTS45vS5Qo"}""", ) # noqa def get_expected_to_str(self): # For some reason, WeixinOAuth2Adapter.complete_login runs this line: # extra_data["nickname"] = nickname.encode("raw_unicode_escape").decode( # "utf-8" # ) return "\\u67d0\\u67d0\\u67d0" ================================================ FILE: tests/apps/socialaccount/providers/windowslive/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/windowslive/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.windowslive.provider import WindowsLiveProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class WindowsLiveTests(OAuth2TestsMixin, TestCase): provider_id = WindowsLiveProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "first_name": "James", "last_name": "Smith", "name": "James Smith", "locale": "en_US", "gender": null, "emails": { "personal": null, "account": "jsmith@example.com", "business": null, "preferred": "jsmith@example.com" }, "link": "https://profile.live.com/", "updated_time": "2014-02-07T00:35:27+0000", "id": "83605e110af6ff98" } """, ) def get_expected_to_str(self): return "jsmith@example.com" ================================================ FILE: tests/apps/socialaccount/providers/xing/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/xing/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.xing.provider import XingProvider from tests.apps.socialaccount.base import OAuthTestsMixin from tests.mocking import MockedResponse class XingTests(OAuthTestsMixin, TestCase): provider_id = XingProvider.id def get_mocked_response(self): return [ MockedResponse( HTTPStatus.OK, """ {"users":[{"id":"20493333_1cd028","active_email":"raymond.penners@example.com", "badges":[],"birth_date":{"year":null,"month":null,"day":null}, "business_address":{"street":null,"zip_code":null,"city":null,"province":null, "country":"NL","email":null,"fax":null,"phone":null,"mobile_phone":null}, "display_name":"Raymond Penners","educational_background": {"primary_school_id":null,"schools":[],"qualifications":[]}, "employment_status":"EMPLOYEE","first_name":"Raymond","gender":"m", "haves":null,"instant_messaging_accounts":{},"interests":null,"languages": {"nl":null},"last_name":"Penners","organisation_member":null, "page_name":"Raymond_Penners", "permalink":"https://www.xing.com/profile/Raymond_Penners", "photo_urls":{"thumb":"https://www.xing.com/img/n/nobody_m.30x40.jpg", "large":"https://www.xing.com/img/n/nobody_m.140x185.jpg","mini_thumb": "https://www.xing.com/img/n/nobody_m.18x24.jpg","maxi_thumb": "https://www.xing.com/img/n/nobody_m.70x93.jpg","medium_thumb": "https://www.xing.com/img/n/nobody_m.57x75.jpg"},"premium_services":[], "private_address":{"street":null,"zip_code":null,"city":null,"province":null, "country":null,"email":"raymond.penners@example.com","fax":null, "phone":null,"mobile_phone":null},"professional_experience": {"primary_company":{"name":null,"url":null,"tag":null,"title":null, "begin_date":null,"end_date":null,"description":null,"industry":"OTHERS", "company_size":null,"career_level":null},"non_primary_companies":[], "awards":[]},"time_zone":{"utc_offset":2.0,"name":"Europe/Berlin"}, "wants":null,"web_profiles":{}}]} """, ) ] def get_expected_to_str(self): return "raymond.penners@example.com" ================================================ FILE: tests/apps/socialaccount/providers/yahoo/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/yahoo/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.yahoo.provider import YahooProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class YahooTests(OAuth2TestsMixin, TestCase): provider_id = YahooProvider.id def get_mocked_response(self): response_data = """ { "sub": "FSVIDUW3D7FSVIDUW3D72F2F", "name": "Jane Doe", "given_name": "Jane", "family_name": "Doe", "preferred_username": "j.doe", "email": "janedoe@example.com", "email_verified": true, "picture": "http://example.com/janedoe/me.jpg" } """ # noqa return MockedResponse(HTTPStatus.OK, response_data) def get_expected_to_str(self): return "janedoe@example.com" ================================================ FILE: tests/apps/socialaccount/providers/yandex/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/yandex/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.yandex.provider import YandexProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class YandexTests(OAuth2TestsMixin, TestCase): provider_id = YandexProvider.id yandex_data = """ { "login": "vasya", "old_social_login": "uid-mmzxrnry", "default_email": "test@yandex.ru", "id": "1000034426", "client_id": "4760187d81bc4b7799476b42b5103713", "emails": [ "test@yandex.ru", "other-test@yandex.ru" ], "openid_identities": [ "http://openid.yandex.ru/vasya/", "http://vasya.ya.ru/" ] }""" def get_mocked_response(self, data=None): if data is None: data = self.yandex_data return MockedResponse(HTTPStatus.OK, data) def get_expected_to_str(self): return "test@yandex.ru" def get_login_response_json(self, with_refresh_token=True): return """ { "access_token":"testac", "refresh_token":"1:GN686QVt0mmakDd9:A4pYuW9LGk0_UnlrMIWklk", "token_type":"bearer", "expires_in":124234123534 }""" ================================================ FILE: tests/apps/socialaccount/providers/ynab/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/ynab/tests.py ================================================ from http import HTTPStatus from requests.exceptions import HTTPError from django.test import TestCase from django.test.client import RequestFactory from django.test.utils import override_settings from django.urls import reverse from allauth.socialaccount.models import SocialToken from allauth.socialaccount.providers.ynab.provider import YNABProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse, mocked_response @override_settings( SOCIALACCOUNT_AUTO_SIGNUP=True, ACCOUNT_SIGNUP_FORM_CLASS=None, ) # ACCOUNT_EMAIL_VERIFICATION=account_settings # .EmailVerificationMethod.MANDATORY) class YNABTests(OAuth2TestsMixin, TestCase): provider_id = YNABProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ {"data": { "user":{ "id": "abcd1234xyz5678" } } } """, ) def get_expected_to_str(self): return "YNAB" def test_ynab_compelete_login_401(self): from allauth.socialaccount.providers.ynab.views import YNABOAuth2Adapter class LessMockedResponse(MockedResponse): def raise_for_status(self): if self.status_code != HTTPStatus.OK: raise HTTPError(None) request = RequestFactory().get( reverse(f"{self.provider.id}_login"), dict(process="login") ) adapter = YNABOAuth2Adapter(request) app = adapter.get_provider().app token = SocialToken(token="some_token") response_with_401 = LessMockedResponse( HTTPStatus.UNAUTHORIZED, """ {"error": { "errors": [{ "domain": "global", "reason": "authError", "message": "Invalid Credentials", "locationType": "header", "location": "Authorization" } ], "code": 401, "message": "Invalid Credentials" } }""", ) with mocked_response(response_with_401): with self.assertRaises(HTTPError): adapter.complete_login(request, app, token) ================================================ FILE: tests/apps/socialaccount/providers/zoho/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/zoho/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.zoho.provider import ZohoProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class ZohoTests(OAuth2TestsMixin, TestCase): provider_id = ZohoProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ {"First_Name":"John","Email":"jdoe@example.com", "Last_Name":"Doe","Display_Name":"JDoee","ZUID":1234567} """, ) def get_expected_to_str(self): return "jdoe@example.com" ================================================ FILE: tests/apps/socialaccount/providers/zoom/__init__.py ================================================ ================================================ FILE: tests/apps/socialaccount/providers/zoom/tests.py ================================================ from http import HTTPStatus from django.test import TestCase from allauth.socialaccount.providers.zoom.provider import ZoomProvider from tests.apps.socialaccount.base import OAuth2TestsMixin from tests.mocking import MockedResponse class ZoomTests(OAuth2TestsMixin, TestCase): provider_id = ZoomProvider.id def get_mocked_response(self): return MockedResponse( HTTPStatus.OK, """ { "id": "KdYKjnimT4KPd8FFgQt9FQ", "first_name": "Jane", "last_name": "Dev", "email": "jane.dev@email.com", "type": 2, "role_name": "Owner", "pmi": 1234567890, "use_pmi": false, "vanity_url": "https://janedevinc.zoom.us/my/janedev", "personal_meeting_url": "https://janedevinc.zoom.us/j/1234567890", "timezone": "America/Denver", "verified": 1, "dept": "", "created_at": "2019-04-05T15:24:32Z", "last_login_time": "2019-12-16T18:02:48Z", "last_client_version": "4.6.12611.1124(mac)", "pic_url": "https://janedev.zoom.us/p/KdYKjnimFR5Td8KKdQt9FQ/19f6430f-...", "host_key": "533895", "jid": "kdykjnimt4kpd8kkdqt9fq@xmpp.zoom.us", "group_ids": [], "im_group_ids": [ "3NXCD9VFTCOUH8LD-QciGw" ], "account_id": "gVcjZnYYRLDbb_MfgHuaxg", "language": "en-US", "phone_country": "US", "phone_number": "+1 1234567891", "status": "active" } """, ) def get_expected_to_str(self): return "jane.dev@email.com" ================================================ FILE: tests/apps/socialaccount/test_adapter.py ================================================ from urllib.parse import parse_qs, urlparse from django.contrib.sites.models import Site from django.urls import reverse from allauth.socialaccount.adapter import DefaultSocialAccountAdapter, get_adapter from allauth.socialaccount.internal import statekit from allauth.socialaccount.models import SocialApp class PrefixStateSocialAccountAdapter(DefaultSocialAccountAdapter): def generate_state_param(self, state: dict) -> str: return f"prefix-{super().generate_state_param(state)}" def test_generate_state_param(settings, client, db, google_provider_settings): settings.SOCIALACCOUNT_ADAPTER = ( "tests.apps.socialaccount.test_adapter.PrefixStateSocialAccountAdapter" ) resp = client.post(reverse("google_login")) parsed = urlparse(resp["location"]) query = parse_qs(parsed.query) state = query["state"][0] assert len(state) == len("prefix-") + statekit.STATE_ID_LENGTH assert state.startswith("prefix-") def test_list_db_based_apps(db, settings): app = SocialApp.objects.create( provider="saml", provider_id="urn:idp-identity-id", client_id="org-slug" ) app.sites.add(Site.objects.get_current()) apps = get_adapter().list_apps(None, provider="saml", client_id="org-slug") assert app.pk in [a.pk for a in apps] def test_list_settings_based_apps(db, settings): settings.SOCIALACCOUNT_PROVIDERS = { "saml": { "APPS": [ { "provider_id": "urn:idp-entity-id", "client_id": "org-slug", } ] } } apps = get_adapter().list_apps(None, provider="saml", client_id="org-slug") assert len(apps) == 1 app = apps[0] assert not app.pk assert app.client_id == "org-slug" def test_get_signup_form_initial_data(sociallogin_factory): sociallogin = sociallogin_factory(email="a@b.com") # it should pick up sociallogin.email_addresses sociallogin.user.email = "" initial_data = get_adapter().get_signup_form_initial_data(sociallogin) assert initial_data["email"] == "a@b.com" ================================================ FILE: tests/apps/socialaccount/test_connect.py ================================================ from http import HTTPStatus from unittest.mock import patch from django.urls import reverse import pytest from pytest_django.asserts import assertTemplateUsed from allauth.socialaccount.internal import flows from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers.base.constants import AuthProcess @pytest.mark.parametrize("reauthentication_required", [False, True]) def test_disconnect(auth_client, user, settings, mailoutbox, reauthentication_required): settings.ACCOUNT_EMAIL_NOTIFICATIONS = True settings.ACCOUNT_REAUTHENTICATION_REQUIRED = reauthentication_required account_to_del = SocialAccount.objects.create( uid="123", provider="other-server", user=user ) account_to_keep = SocialAccount.objects.create( uid="456", provider="other-server", user=user ) resp = auth_client.get(reverse("socialaccount_connections")) assertTemplateUsed(resp, "socialaccount/connections.html") resp = auth_client.post( reverse("socialaccount_connections"), {"account": account_to_del.pk} ) if reauthentication_required: assert SocialAccount.objects.filter(pk=account_to_del.pk).exists() assert SocialAccount.objects.filter(pk=account_to_keep.pk).exists() else: assert not SocialAccount.objects.filter(pk=account_to_del.pk).exists() assert SocialAccount.objects.filter(pk=account_to_keep.pk).exists() assert len(mailoutbox) == 1 assert mailoutbox[0].subject == "[example.com] Third-Party Account Disconnected" def test_connect_with_reauthentication( auth_client, user, provider_callback_response, settings, user_password ): settings.ACCOUNT_REAUTHENTICATION_REQUIRED = True resp = provider_callback_response(auth_client, process="connect") assert not SocialAccount.objects.filter(user=user).exists() assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_reauthenticate") resp = auth_client.post(resp["location"], {"password": user_password}) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("socialaccount_connections") assert SocialAccount.objects.filter(user=user).exists() def test_connect( auth_client, user, provider_callback_response, settings, user_password, mailoutbox ): settings.ACCOUNT_EMAIL_NOTIFICATIONS = True settings.ACCOUNT_REAUTHENTICATION_REQUIRED = False resp = provider_callback_response(auth_client, process="connect") assert resp.status_code == HTTPStatus.FOUND assert SocialAccount.objects.filter(user=user).exists() assert resp["location"] == reverse("socialaccount_connections") assert len(mailoutbox) == 1 assert mailoutbox[0].subject == "[example.com] Third-Party Account Connected" @pytest.mark.parametrize( "email_authentication,account_exists, expected_action", [ (False, False, "added"), (False, True, "updated"), (True, False, "added"), (True, True, "updated"), ], ) def test_connect_vs_email_authentication( request_factory, sociallogin_factory, user, settings, email_authentication, account_exists, expected_action, ): settings.SOCIALACCOUNT_EMAIL_AUTHENTICATION = email_authentication sociallogin = sociallogin_factory(email=user.email, provider="unittest-server") if account_exists: account = sociallogin.account account.user = user account.save() sociallogin.state["process"] = AuthProcess.CONNECT request = request_factory.get("/") request.user = user with patch( "allauth.account.adapter.DefaultAccountAdapter.add_message" ) as add_message: flows.login.complete_login(request, sociallogin) assert add_message.call_args[1]["message_context"]["action"] == expected_action assert SocialAccount.objects.filter(user=user, uid=sociallogin.account.uid).exists() ================================================ FILE: tests/apps/socialaccount/test_login.py ================================================ import copy from http import HTTPStatus from unittest.mock import ANY, patch from django.contrib.auth import get_user_model from django.contrib.auth.models import AnonymousUser from django.urls import reverse import pytest from pytest_django.asserts import assertTemplateUsed from allauth.account.authentication import AUTHENTICATION_METHODS_SESSION_KEY from allauth.core import context from allauth.socialaccount.helpers import complete_social_login from allauth.socialaccount.models import SocialAccount, SocialToken from allauth.socialaccount.providers.base import AuthProcess @pytest.mark.parametrize("with_emailaddress", [False, True]) @pytest.mark.parametrize("auto_connect", [False, True]) @pytest.mark.parametrize("setting", ["off", "on-global", "on-provider"]) def test_email_authentication( db, setting, settings, user_factory, sociallogin_factory, client, request_factory, mailoutbox, auto_connect, with_emailaddress, ): """Tests that when an already existing email is given at the social signup form, enumeration preventation kicks in. """ settings.ACCOUNT_EMAIL_REQUIRED = True settings.ACCOUNT_UNIQUE_EMAIL = True settings.ACCOUNT_USERNAME_REQUIRED = False settings.ACCOUNT_LOGIN_METHODS = {"email"} settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.SOCIALACCOUNT_AUTO_SIGNUP = True settings.SOCIALACCOUNT_STORE_TOKENS = True if setting == "on-global": settings.SOCIALACCOUNT_EMAIL_AUTHENTICATION = True elif setting == "on-provider": settings.SOCIALACCOUNT_PROVIDERS = copy.deepcopy( settings.SOCIALACCOUNT_PROVIDERS ) settings.SOCIALACCOUNT_PROVIDERS["openid_connect"][ "EMAIL_AUTHENTICATION" ] = True else: settings.SOCIALACCOUNT_EMAIL_AUTHENTICATION = False settings.SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT = auto_connect user = user_factory(with_emailaddress=with_emailaddress) assert user.has_usable_password() sociallogin = sociallogin_factory( email=user.email, provider="unittest-server", with_token=True ) request = request_factory.get("/") request.user = AnonymousUser() with context.request_context(request): with patch( "allauth.socialaccount.signals.social_account_updated.send" ) as updated_signal: with patch( "allauth.socialaccount.signals.social_account_added.send" ) as added_signal: resp = complete_social_login(request, sociallogin) user.refresh_from_db() if setting == "off": assert resp["location"] == reverse("account_email_verification_sent") assert not added_signal.called assert not updated_signal.called else: if with_emailaddress: assert resp["location"] == "/accounts/profile/" assert user.has_usable_password() else: assert not user.has_usable_password() # This should be improved. The provider vouches for the fact that # the user verified the email, so we can mark it as such locally as # well. # user.email is set, but not verified. assert resp["location"] == reverse("account_email_verification_sent") assert get_user_model().objects.count() == 1 assert SocialAccount.objects.filter(user=user.pk).exists() == auto_connect assert ( SocialToken.objects.filter(account__user=user.pk).exists() == auto_connect ) assert added_signal.called == auto_connect assert not updated_signal.called def test_login_cancelled(client): resp = client.get(reverse("socialaccount_login_cancelled")) assert resp.status_code == HTTPStatus.OK assertTemplateUsed(resp, "socialaccount/login_cancelled.html") @pytest.mark.parametrize("store_tokens", [False, True]) @pytest.mark.parametrize( "process,did_record", [ (AuthProcess.LOGIN, True), (AuthProcess.CONNECT, False), ], ) def test_record_authentication( db, sociallogin_factory, client, request_factory, user, process, did_record, store_tokens, settings, ): settings.SOCIALACCOUNT_STORE_TOKENS = store_tokens sociallogin = sociallogin_factory(provider="unittest-server", uid="123") sociallogin.state["process"] = process sociallogin.token = SocialToken( app=sociallogin.provider.app, token="123", token_secret="456" ) SocialAccount.objects.create(user=user, uid="123", provider="unittest-server") request = request_factory.get("/") request.user = AnonymousUser() with context.request_context(request): complete_social_login(request, sociallogin) if did_record: assert request.session[AUTHENTICATION_METHODS_SESSION_KEY] == [ { "at": ANY, "provider": sociallogin.account.provider, "method": "socialaccount", "uid": "123", } ] else: assert AUTHENTICATION_METHODS_SESSION_KEY not in request.session assert ( SocialToken.objects.filter( account__uid="123", token="123", token_secret="456" ).exists() == store_tokens ) ================================================ FILE: tests/apps/socialaccount/test_phone.py ================================================ from http import HTTPStatus from django.conf import settings from django.contrib.auth import get_user_model from django.urls import reverse, reverse_lazy import pytest from allauth.account.adapter import get_adapter as get_account_adapter from allauth.socialaccount.models import SocialAccount @pytest.mark.parametrize( "phone_verified, phone_valid, phone_taken, expected_url", [ (True, True, False, settings.LOGIN_REDIRECT_URL), (False, True, False, reverse_lazy("account_verify_phone")), (True, False, False, reverse_lazy("socialaccount_signup")), (True, True, True, reverse_lazy("socialaccount_signup")), ], ) def test_signup_with_phone( db, settings_impacting_urls, client, phone, phone_verified, phone_valid, phone_taken, expected_url, user_factory, ): if phone_taken: user_factory(phone=phone) with settings_impacting_urls( SOCIALACCOUNT_AUTO_SIGNUP=True, ACCOUNT_LOGIN_METHODS=("phone",), ACCOUNT_SIGNUP_FIELDS=["phone*"], ): resp = client.post(reverse("dummy_login")) assert resp.status_code == HTTPStatus.FOUND assert resp["location"].startswith(f"{reverse('dummy_authenticate')}?state=") resp = client.post( resp["location"], { "id": "123", "email": "a@b.com", "email_verified": True, "phone": phone if phone_valid else "*INVALID*", "phone_verified": phone_verified, }, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == expected_url if not phone_valid or phone_taken: return get_user_model().objects.filter(email="a@b.com").exists() socialaccount = SocialAccount.objects.get(uid="123") account = socialaccount.get_provider_account() assert account.to_str() == "a@b.com" assert get_account_adapter().get_phone(socialaccount.user) == ( phone, phone_verified, ) ================================================ FILE: tests/apps/socialaccount/test_registry.py ================================================ from django.apps import AppConfig, apps from django.test import TestCase from django.test.utils import override_settings from allauth.socialaccount import providers class CustomFacebookAppConfig(AppConfig): name = "allauth.socialaccount.providers.facebook" label = "allauth_facebook" class ProviderRegistryTests(TestCase): @override_settings( INSTALLED_APPS=[ "allauth.socialaccount.providers.facebook", ] ) def test_load_provider_with_default_app_config(self): registry = providers.ProviderRegistry() provider_list = registry.get_class_list() self.assertTrue(registry.loaded) self.assertEqual(1, len(provider_list)) self.assertTrue( issubclass( provider_list[0], providers.facebook.provider.FacebookProvider, ) ) app_config_list = list(apps.get_app_configs()) self.assertEqual(1, len(app_config_list)) app_config = app_config_list[0] self.assertEqual("allauth.socialaccount.providers.facebook", app_config.name) self.assertEqual("facebook", app_config.label) @override_settings( INSTALLED_APPS=[ "tests.apps.socialaccount.test_registry.CustomFacebookAppConfig", ] ) def test_load_provider_with_custom_app_config(self): registry = providers.ProviderRegistry() provider_list = registry.get_class_list() self.assertTrue(registry.loaded) self.assertEqual(1, len(provider_list)) self.assertTrue( issubclass( provider_list[0], providers.facebook.provider.FacebookProvider, ) ) app_config_list = list(apps.get_app_configs()) self.assertEqual(1, len(app_config_list)) app_config = app_config_list[0] self.assertEqual("allauth.socialaccount.providers.facebook", app_config.name) self.assertEqual("allauth_facebook", app_config.label) ================================================ FILE: tests/apps/socialaccount/test_signup.py ================================================ from http import HTTPStatus from unittest.mock import ANY, patch from django.conf import settings from django.contrib.auth import get_user_model from django.contrib.auth.models import AnonymousUser from django.urls import reverse import pytest from pytest_django.asserts import assertFormError, assertRedirects from allauth.account import app_settings as account_settings from allauth.account.authentication import AUTHENTICATION_METHODS_SESSION_KEY from allauth.account.models import EmailAddress from allauth.account.utils import user_email, user_username from allauth.core import context from allauth.socialaccount.helpers import complete_social_login from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.views import signup @pytest.fixture def setup_sociallogin_flow(request_factory): def f(client, sociallogin): request = request_factory.get("/") request.user = AnonymousUser() with context.request_context(request): resp = complete_social_login(request, sociallogin) session = client.session for k, v in request.session.items(): session[k] = v session.save() return resp return f @pytest.fixture def email_address_clash( request_factory, sociallogin_factory, twitter_provider_settings ): def _email_address_clash(username, email): User = get_user_model() # Some existig user exi_user = User() user_username(exi_user, "test") exi_user_email = "test@example.com" user_email(exi_user, exi_user_email) exi_user.save() EmailAddress.objects.create( user=exi_user, email=exi_user_email, verified=True, primary=True ) # A social user being signed up... sociallogin = sociallogin_factory( provider="twitter", username=username, email=email ) # Signing up, should pop up the social signup form request = request_factory.get("/accounts/twitter/login/callback/") request.user = AnonymousUser() with context.request_context(request): resp = complete_social_login(request, sociallogin) return request, resp return _email_address_clash def test_email_address_created( settings, db, client, setup_sociallogin_flow, sociallogin_factory ): settings.SOCIALACCOUNT_AUTO_SIGNUP = True settings.ACCOUNT_SIGNUP_FORM_CLASS = None settings.ACCOUNT_EMAIL_VERIFICATION = account_settings.EmailVerificationMethod.NONE sociallogin = sociallogin_factory( email="test@example.com", email_verified=False, username="test" ) setup_sociallogin_flow(client, sociallogin) user = get_user_model().objects.get( **{account_settings.USER_MODEL_USERNAME_FIELD: "test"} ) assert SocialAccount.objects.filter(user=user, uid=sociallogin.account.uid).exists() assert EmailAddress.objects.filter(user=user, email=user_email(user)).exists() def test_email_address_clash_username_required( db, client, settings, email_address_clash ): """Test clash on both username and email""" settings.ACCOUNT_EMAIL_REQUIRED = True settings.ACCOUNT_UNIQUE_EMAIL = True settings.ACCOUNT_USERNAME_REQUIRED = True settings.ACCOUNT_LOGIN_METHODS = {"email"} settings.SOCIALACCOUNT_AUTO_SIGNUP = True request, resp = email_address_clash("test", "test@example.com") assert resp["location"] == reverse("socialaccount_signup") # POST different username/email to social signup form request.method = "POST" request.POST = {"username": "other", "email": "other@example.com"} with context.request_context(request): resp = signup(request) assert resp["location"] == "/accounts/profile/" user = get_user_model().objects.get( **{account_settings.USER_MODEL_EMAIL_FIELD: "other@example.com"} ) assert user_username(user) == "other" def test_email_address_clash_username_not_required(db, settings, email_address_clash): """Test clash while username is not required""" settings.ACCOUNT_EMAIL_REQUIRED = True settings.ACCOUNT_UNIQUE_EMAIL = True settings.ACCOUNT_USERNAME_REQUIRED = False settings.ACCOUNT_LOGIN_METHODS = {"email"} settings.SOCIALACCOUNT_AUTO_SIGNUP = True request, resp = email_address_clash("test", "test@example.com") assert resp["location"] == reverse("socialaccount_signup") # POST email to social signup form (username not present) request.method = "POST" request.POST = {"email": "other@example.com"} with context.request_context(request): resp = signup(request) assert resp["location"] == "/accounts/profile/" user = get_user_model().objects.get( **{account_settings.USER_MODEL_EMAIL_FIELD: "other@example.com"} ) assert user_username(user) != "test" def test_email_address_clash_username_auto_signup(db, settings, email_address_clash): settings.ACCOUNT_EMAIL_REQUIRED = True settings.ACCOUNT_UNIQUE_EMAIL = True settings.ACCOUNT_USERNAME_REQUIRED = False settings.ACCOUNT_LOGIN_METHODS = {"email"} settings.SOCIALACCOUNT_AUTO_SIGNUP = True # Clash on username, but auto signup still works request, resp = email_address_clash("test", "other@example.com") assert resp["location"] == "/accounts/profile/" user = get_user_model().objects.get( **{account_settings.USER_MODEL_EMAIL_FIELD: "other@example.com"} ) assert user_username(user) != "test" def test_populate_username_in_blacklist( db, settings, request_factory, sociallogin_factory, twitter_provider_settings ): settings.ACCOUNT_EMAIL_REQUIRED = True settings.ACCOUNT_USERNAME_BLACKLIST = ["username", "username1", "username2"] settings.ACCOUNT_UNIQUE_EMAIL = True settings.ACCOUNT_USERNAME_REQUIRED = True settings.ACCOUNT_LOGIN_METHODS = {"email"} settings.SOCIALACCOUNT_AUTO_SIGNUP = True request = request_factory.get("/accounts/twitter/login/callback/") request.user = AnonymousUser() sociallogin = sociallogin_factory( provider="twitter", username="username", email="username@example.com" ) with context.request_context(request): complete_social_login(request, sociallogin) assert request.user.username not in account_settings.USERNAME_BLACKLIST def test_verified_email_change_at_signup( db, client, settings, sociallogin_factory, setup_sociallogin_flow ): """ Test scenario for when the user changes email at social signup. Current behavior is that both the unverified and verified email are added, and that the user is allowed to pass because he did provide a verified one. """ settings.ACCOUNT_EMAIL_REQUIRED = True settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.ACCOUNT_UNIQUE_EMAIL = True settings.ACCOUNT_USERNAME_REQUIRED = False settings.ACCOUNT_LOGIN_METHODS = {"email"} settings.SOCIALACCOUNT_AUTO_SIGNUP = False sociallogin = sociallogin_factory(email="verified@example.com") setup_sociallogin_flow(client, sociallogin) resp = client.get(reverse("socialaccount_signup")) form = resp.context["form"] assert form["email"].value() == "verified@example.com" resp = client.post( reverse("socialaccount_signup"), data={"email": "unverified@example.org"}, ) assertRedirects(resp, "/accounts/profile/", fetch_redirect_response=False) user = get_user_model().objects.all()[0] assert user_email(user) == "verified@example.com" assert EmailAddress.objects.filter( user=user, email="verified@example.com", verified=True, primary=True, ).exists() assert EmailAddress.objects.filter( user=user, email="unverified@example.org", verified=False, primary=False, ).exists() def test_unverified_email_change_at_signup( db, client, settings, sociallogin_factory, setup_sociallogin_flow ): """ Test scenario for when the user changes email at social signup, while his provider did not provide a verified email. In that case, email verification will kick in. Here, both email addresses are added as well. """ settings.ACCOUNT_EMAIL_REQUIRED = True settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.ACCOUNT_UNIQUE_EMAIL = True settings.ACCOUNT_USERNAME_REQUIRED = False settings.ACCOUNT_LOGIN_METHODS = {"email"} settings.SOCIALACCOUNT_AUTO_SIGNUP = False User = get_user_model() sociallogin = sociallogin_factory( email="unverified@example.com", email_verified=False ) setup_sociallogin_flow(client, sociallogin) resp = client.get(reverse("socialaccount_signup")) form = resp.context["form"] assert form["email"].value() == "unverified@example.com" resp = client.post( reverse("socialaccount_signup"), data={"email": "unverified@example.org"}, ) assertRedirects(resp, reverse("account_email_verification_sent")) user = User.objects.all()[0] assert user_email(user) == "unverified@example.org" assert EmailAddress.objects.filter( user=user, email="unverified@example.com", verified=False, primary=False, ).exists() assert EmailAddress.objects.filter( user=user, email="unverified@example.org", verified=False, primary=True, ).exists() def test_unique_email_validation_signup( db, client, sociallogin_factory, settings, setup_sociallogin_flow ): settings.ACCOUNT_PREVENT_ENUMERATION = False settings.ACCOUNT_EMAIL_REQUIRED = True settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.ACCOUNT_UNIQUE_EMAIL = True settings.ACCOUNT_USERNAME_REQUIRED = False settings.ACCOUNT_LOGIN_METHODS = {"email"} settings.SOCIALACCOUNT_AUTO_SIGNUP = False User = get_user_model() email = "me@example.com" user = User.objects.create(email=email) EmailAddress.objects.create(email=email, user=user, verified=True) sociallogin = sociallogin_factory(email="me@example.com") setup_sociallogin_flow(client, sociallogin) resp = client.get(reverse("socialaccount_signup")) form = resp.context["form"] assert form["email"].value() == email resp = client.post(reverse("socialaccount_signup"), data={"email": email}) assertFormError( resp.context["form"], "email", "An account already exists with this email address." " Please sign in to that account first, then connect" " your Unittest Server account.", ) def test_social_account_taken_at_signup( db, client, sociallogin_factory, settings, setup_sociallogin_flow ): """ Test scenario for when the user signs up with a social account and uses email address in that social account. But upon seeing the verification screen, they realize that email address is somehow unusable for them, and so backs up and enters a different email address (and is forced to choose a new username) while providing the same social account token which is owned by their first attempt. """ settings.ACCOUNT_EMAIL_REQUIRED = True settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.ACCOUNT_UNIQUE_EMAIL = True settings.ACCOUNT_USERNAME_REQUIRED = True settings.ACCOUNT_LOGIN_METHODS = {"email"} settings.SOCIALACCOUNT_AUTO_SIGNUP = False User = get_user_model() sociallogin = sociallogin_factory(email="me1@example.com", email_verified=False) setup_sociallogin_flow(client, sociallogin) resp = client.get(reverse("socialaccount_signup")) form = resp.context["form"] assert form["email"].value() == "me1@example.com" resp = client.post( reverse("socialaccount_signup"), data={"username": "me1", "email": "me1@example.com"}, ) assert resp.status_code == HTTPStatus.FOUND assert User.objects.count() == 1 assert SocialAccount.objects.count() == 1 resp = client.get(reverse("socialaccount_signup")) assertRedirects(resp, reverse("account_login")) def test_email_address_required_missing_from_sociallogin( db, settings, sociallogin_factory, client, setup_sociallogin_flow ): """Tests that when the email address is missing from the sociallogin email verification kicks in. """ settings.ACCOUNT_EMAIL_REQUIRED = True settings.ACCOUNT_UNIQUE_EMAIL = True settings.ACCOUNT_USERNAME_REQUIRED = False settings.ACCOUNT_LOGIN_METHODS = {"email"} settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.SOCIALACCOUNT_AUTO_SIGNUP = True sociallogin = sociallogin_factory(with_email=False) resp = setup_sociallogin_flow(client, sociallogin) assert resp["location"] == reverse("socialaccount_signup") resp = client.post(reverse("socialaccount_signup"), {"email": "other@example.org"}) assert resp["location"] == reverse("account_email_verification_sent") def test_email_address_conflict_at_social_signup_form( db, settings, user_factory, sociallogin_factory, client, setup_sociallogin_flow, mailoutbox, ): """Tests that when an already existing email is given at the social signup form, enumeration preventation kicks in. """ settings.ACCOUNT_EMAIL_REQUIRED = True settings.ACCOUNT_UNIQUE_EMAIL = True settings.ACCOUNT_USERNAME_REQUIRED = False settings.ACCOUNT_LOGIN_METHODS = {"email"} settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.SOCIALACCOUNT_AUTO_SIGNUP = True user = user_factory() sociallogin = sociallogin_factory(with_email=False) resp = setup_sociallogin_flow(client, sociallogin) # Auto signup does not kick in as the `sociallogin` does not have an email. assert resp["location"] == reverse("socialaccount_signup") # Here, we input the already existing email. resp = client.post(reverse("socialaccount_signup"), {"email": user.email}) assert mailoutbox[0].subject == "[example.com] Account Already Exists" assert resp["location"] == reverse("account_email_verification_sent") def test_email_address_conflict_during_auto_signup( db, settings, user_factory, sociallogin_factory, client, mailoutbox, setup_sociallogin_flow, ): """Tests that when an already existing email is received from the provider, enumeration preventation kicks in. """ settings.ACCOUNT_EMAIL_REQUIRED = True settings.ACCOUNT_UNIQUE_EMAIL = True settings.ACCOUNT_USERNAME_REQUIRED = False settings.ACCOUNT_LOGIN_METHODS = {"email"} settings.ACCOUNT_EMAIL_VERIFICATION = "mandatory" settings.SOCIALACCOUNT_AUTO_SIGNUP = True user = user_factory() sociallogin = sociallogin_factory(email=user.email, with_email=True) resp = setup_sociallogin_flow(client, sociallogin) assert resp["location"] == reverse("account_email_verification_sent") assert mailoutbox[0].subject == "[example.com] Account Already Exists" def test_email_address_conflict_removes_conflicting_email( db, settings, user_factory, sociallogin_factory, client, mailoutbox, setup_sociallogin_flow, ): """Tests that when an already existing email is given at the social signup form, enumeration preventation kicks in. """ settings.ACCOUNT_EMAIL_REQUIRED = True settings.ACCOUNT_UNIQUE_EMAIL = True settings.ACCOUNT_USERNAME_REQUIRED = False settings.ACCOUNT_LOGIN_METHODS = {"email"} settings.ACCOUNT_EMAIL_VERIFICATION = "optional" settings.SOCIALACCOUNT_AUTO_SIGNUP = True settings.SOCIALACCOUNT_EMAIL_AUTHENTICATION = False user = user_factory(email_verified=False) sociallogin = sociallogin_factory(email=user.email, email_verified=False) resp = setup_sociallogin_flow(client, sociallogin) # Auto signup does not kick in as the `sociallogin` has a conflicting email. assert resp["location"] == reverse("socialaccount_signup") # Here, we input the already existing email. resp = client.post(reverse("socialaccount_signup"), {"email": "other@email.org"}) assert mailoutbox[0].subject == "[example.com] Please Confirm Your Email Address" assert resp["location"] == settings.LOGIN_REDIRECT_URL assert EmailAddress.objects.filter(email=user.email).count() == 1 def test_signup_closed( settings, db, client, setup_sociallogin_flow, sociallogin_factory, ): sociallogin = sociallogin_factory( email="test@example.com", email_verified=False, username="test" ) with patch( "allauth.socialaccount.adapter.DefaultSocialAccountAdapter.is_open_for_signup" ) as iofs: iofs.return_value = False resp = setup_sociallogin_flow(client, sociallogin) assert b"Sign Up Closed" in resp.content assert not get_user_model().objects.exists() def test_authentication_records(client, db): resp = client.post(reverse("dummy_login")) assert resp.status_code == HTTPStatus.FOUND resp = client.post( resp["location"], {"id": "123", "email": "a@b.com", "email_verified": True}, ) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == settings.LOGIN_REDIRECT_URL assert client.session[AUTHENTICATION_METHODS_SESSION_KEY] == [ { "at": ANY, "method": "socialaccount", "provider": "dummy", "uid": "123", } ] ================================================ FILE: tests/apps/socialaccount/test_utils.py ================================================ from django.contrib.auth import get_user_model from django.test import TestCase from django.test.utils import override_settings from allauth.socialaccount.models import SocialAccount class UtilTests(TestCase): def test_social_account_str_default(self): User = get_user_model() user = User(username="test") sa = SocialAccount(user=user) self.assertEqual("test", str(sa)) def socialaccount_str_custom_formatter(socialaccount): return f"A custom str builder for {socialaccount.user}" @override_settings( SOCIALACCOUNT_SOCIALACCOUNT_STR=socialaccount_str_custom_formatter ) def test_social_account_str_customized(self): User = get_user_model() user = User(username="test") sa = SocialAccount(user=user) self.assertEqual("A custom str builder for test", str(sa)) ================================================ FILE: tests/apps/test_utils.py ================================================ from http import HTTPStatus from django.test import RequestFactory, TestCase from django.utils.http import base36_to_int, int_to_base36 from django.views import csrf from allauth import app_settings, utils class BasicTests(TestCase): def setUp(self): self.factory = RequestFactory() def test_generate_unique_username(self): examples = [ ("a.b-c@example.com", "a.b-c"), ("Üsêrnamê", "username"), ("User Name", "user_name"), ("", "user"), ] for input, username in examples: self.assertEqual(utils.generate_unique_username([input]), username) def test_build_absolute_uri(self): request = None if not app_settings.SITES_ENABLED: request = self.factory.get("/") request.META["SERVER_NAME"] = "example.com" self.assertEqual( utils.build_absolute_uri(request, "/foo"), "http://example.com/foo" ) self.assertEqual( utils.build_absolute_uri(request, "/foo", protocol="ftp"), "ftp://example.com/foo", ) self.assertEqual( utils.build_absolute_uri(request, "http://foo.com/bar"), "http://foo.com/bar", ) def test_int_to_base36(self): n = 55798679658823689999 b36 = "brxk553wvxbf3" assert int_to_base36(n) == b36 assert base36_to_int(b36) == n def test_templatetag_with_csrf_failure(self): # Generate a fictitious GET request if not app_settings.SOCIALACCOUNT_ENABLED: return from allauth.socialaccount.models import SocialApp app = SocialApp.objects.create(provider="google") if app_settings.SITES_ENABLED: from django.contrib.sites.models import Site app.sites.add(Site.objects.get_current()) request = self.factory.get("/tests/test_403_csrf.html") # Simulate a CSRF failure by calling the View directly # This template is using the `provider_login_url` templatetag response = csrf.csrf_failure(request, template_name="test_403_csrf.html") # Ensure that CSRF failures with this template # tag succeed with the expected 403 response self.assertEqual(response.status_code, HTTPStatus.FORBIDDEN) ================================================ FILE: tests/apps/usersessions/__init__.py ================================================ ================================================ FILE: tests/apps/usersessions/test_middleware.py ================================================ from unittest.mock import Mock from django.contrib.auth.models import AnonymousUser from django.test.utils import override_settings import pytest from allauth.usersessions.middleware import UserSessionsMiddleware from allauth.usersessions.models import UserSession from allauth.usersessions.signals import session_client_changed def test_mw_without_request_user(rf, db, settings): settings.USERSESSIONS_TRACK_ACTIVITY = True mw = UserSessionsMiddleware(lambda request: None) request = rf.get("/") mw(request) assert UserSession.objects.count() == 0 @pytest.mark.parametrize("track_activity", [False, True]) def test_mw_with_request_user(rf, db, settings, user, track_activity): settings.USERSESSIONS_TRACK_ACTIVITY = track_activity mw = UserSessionsMiddleware(lambda request: None) request = rf.get("/") request.user = user request.session = Mock() request.session.session_key = "sess-123" mw(request) assert ( UserSession.objects.filter(session_key="sess-123", user=user).exists() is track_activity ) def test_mw_with_anonymous_request_user(rf, db, settings): settings.USERSESSIONS_TRACK_ACTIVITY = True mw = UserSessionsMiddleware(lambda request: None) request = rf.get("/") request.user = AnonymousUser() request.session = Mock() request.session.session_key = "sess-123" mw(request) assert not UserSession.objects.exists() @override_settings(USERSESSIONS_TRACK_ACTIVITY=True) def test_mw_change_ip_and_useragent(rf, db, user): mw = UserSessionsMiddleware(lambda request: None) # First request request1 = rf.get("/") request1.user = user request1.session = Mock() request1.session.session_key = "sess-123" request1.META["HTTP_USER_AGENT"] = "Old User Agent" request1.META["REMOTE_ADDR"] = "1.1.1.1" mw(request1) # Second request with changed IP and User Agent request2 = rf.get("/") request2.user = user request2.session = Mock() request2.session.session_key = "sess-123" request2.META["HTTP_USER_AGENT"] = "New User Agent" request2.META["REMOTE_ADDR"] = "2.2.2.2" # Set up signal receiver signal_received = [] def signal_handler(sender, request, from_session, to_session, **kwargs): signal_received.append((from_session, to_session)) session_client_changed.connect(signal_handler) # Process second request mw(request2) # Check if UserSession was updated user_session = UserSession.objects.get(session_key="sess-123", user=user) assert user_session.ip == "2.2.2.2" assert user_session.user_agent == "New User Agent" # Check if signal was triggered assert len(signal_received) == 1 from_session, to_session = signal_received[0] assert from_session.ip == "1.1.1.1" assert from_session.user_agent == "Old User Agent" assert to_session.ip == "2.2.2.2" assert to_session.user_agent == "New User Agent" # Clean up signal connection session_client_changed.disconnect(signal_handler) ================================================ FILE: tests/apps/usersessions/test_views.py ================================================ from http import HTTPStatus from django.test import Client from django.urls import reverse import pytest from allauth.usersessions.models import UserSession def test_overall_flow(user, user_password): firefox = Client(HTTP_USER_AGENT="Mozilla Firefox") nyxt = Client(HTTP_USER_AGENT="Nyxt") for client in [firefox, nyxt]: resp = client.post( reverse("account_login"), {"login": user.username, "password": user_password}, ) assert resp.status_code == HTTPStatus.FOUND assert UserSession.objects.filter(user=user).count() == 2 sessions = list(UserSession.objects.filter(user=user).order_by("pk")) assert sessions[0].user_agent == "Mozilla Firefox" assert sessions[1].user_agent == "Nyxt" for client in [firefox, nyxt]: resp = client.get(reverse("usersessions_list")) assert resp.status_code == HTTPStatus.OK resp = firefox.post(reverse("usersessions_list")) assert resp.status_code == HTTPStatus.FOUND assert UserSession.objects.filter(user=user).count() == 1 assert UserSession.objects.filter(user=user, pk=sessions[0].pk).exists() assert not UserSession.objects.filter(user=user, pk=sessions[1].pk).exists() resp = nyxt.get(reverse("usersessions_list")) assert resp.status_code == HTTPStatus.FOUND assert resp["location"] == reverse("account_login") + "?next=" + reverse( "usersessions_list" ) @pytest.mark.parametrize("logout_on_passwd_change", [True, False]) def test_change_password_updates_user_session( settings, logout_on_passwd_change, client, user, user_password, password_factory ): settings.ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = logout_on_passwd_change resp = client.post( reverse("account_login"), {"login": user.username, "password": user_password}, ) assert resp.status_code == HTTPStatus.FOUND assert len(UserSession.objects.purge_and_list(user)) == 1 new_password = password_factory() resp = client.post( reverse("account_change_password"), { "oldpassword": user_password, "password1": new_password, "password2": new_password, }, ) assert len(UserSession.objects.purge_and_list(user)) == ( 0 if logout_on_passwd_change else 1 ) ================================================ FILE: tests/conftest.py ================================================ import importlib import json import os import random import re import sys import time import uuid from contextlib import contextmanager from pathlib import Path from unittest.mock import Mock, PropertyMock, patch from django.contrib.auth import get_user_model from django.contrib.messages.middleware import MessageMiddleware from django.contrib.sessions.middleware import SessionMiddleware from django.urls import clear_url_caches, set_urlconf import pytest from allauth.account.models import EmailAddress from allauth.account.utils import user_email, user_pk_to_url_str, user_username from allauth.core import context from allauth.socialaccount.internal import statekit from allauth.socialaccount.providers.base.constants import AuthProcess def pytest_collection_modifyitems(config, items): if config.getoption("--ds") == "tests.projects.headless_only.settings": removed_items = [] for item in items: if not item.location[0].startswith("tests/apps/headless"): removed_items.append(item) for item in removed_items: items.remove(item) @pytest.fixture def user(user_factory): return user_factory() @pytest.fixture def auth_client(client, user): client.force_login(user) return client @pytest.fixture def password_factory(): def f(): return str(uuid.uuid4()) return f @pytest.fixture def user_password(password_factory): return password_factory() @pytest.fixture def email_verified(): return True @pytest.fixture def user_factory(email_factory, db, user_password, email_verified): def factory( email=None, username=None, commit=True, with_email=True, email_verified=email_verified, password=None, phone=None, phone_verified=False, with_emailaddress=True, with_totp=False, ): from allauth.account.adapter import get_adapter if not username: username = uuid.uuid4().hex if not email and with_email: email = email_factory(username=username) User = get_user_model() user = User() if password == "!": # nosec user.password = password else: user.set_password(user_password if password is None else password) user_username(user, username) user_email(user, email or "") if commit: user.save() if email and with_emailaddress: EmailAddress.objects.create( user=user, email=email.lower(), verified=email_verified, primary=True, ) if with_totp: from allauth.mfa.totp.internal import auth auth.TOTP.activate(user, auth.generate_totp_secret()) if phone: get_adapter().set_phone(user, phone, phone_verified) return user return factory @pytest.fixture def email_factory(): def factory(username=None, email=None, mixed_case=False): if email is None: if not username: username = uuid.uuid4().hex email = f"{username}@{uuid.uuid4().hex}.org" if mixed_case: email = "".join( [random.choice([c.upper(), c.lower()]) for c in email] # nosec ) else: email = email.lower() return email return factory @pytest.fixture def reauthentication_bypass(): @contextmanager def f(): with patch( "allauth.account.internal.flows.reauthentication.did_recently_authenticate" ) as m: m.return_value = True yield return f @pytest.fixture def webauthn_authentication_bypass(): @contextmanager def f(authenticator): from fido2.utils import websafe_encode from allauth.mfa.adapter import get_adapter with patch( "allauth.mfa.webauthn.internal.auth.WebAuthn.authenticator_data", new_callable=PropertyMock, ) as ad_m: with patch("fido2.server.Fido2Server.authenticate_begin") as ab_m: ab_m.return_value = ({}, {"state": "dummy"}) with patch("fido2.server.Fido2Server.authenticate_complete") as ac_m: with patch( "allauth.mfa.webauthn.internal.auth.parse_authentication_response" ) as m: user_handle = ( get_adapter().get_public_key_credential_user_entity( authenticator.user )["id"] ) authenticator_data = Mock() authenticator_data.credential_data.credential_id = ( "credential_id" ) ad_m.return_value = authenticator_data m.return_value = Mock() binding = Mock() binding.credential_id = "credential_id" ac_m.return_value = binding yield json.dumps( {"response": {"userHandle": websafe_encode(user_handle)}} ) return f @pytest.fixture def webauthn_registration_bypass(): @contextmanager def f(user, passwordless): with patch("fido2.server.Fido2Server.register_complete") as rc_m: with patch( "allauth.mfa.webauthn.internal.auth.parse_registration_response" ) as m: m.return_value = Mock() class FakeAuthenticatorData(bytes): def is_user_verified(self): return passwordless binding = FakeAuthenticatorData(b"binding") rc_m.return_value = binding yield json.dumps( { "authenticatorAttachment": "cross-platform", "clientExtensionResults": {"credProps": {"rk": passwordless}}, "id": "123", "rawId": "456", "response": { "attestationObject": "ao", "clientDataJSON": "cdj", "transports": ["usb"], }, "type": "public-key", } ) return f @pytest.fixture(autouse=True) def clear_context_request(): context._request_var.set(None) @pytest.fixture def enable_cache(settings): from django.core.cache import cache settings.CACHES = { "default": { "BACKEND": "django.core.cache.backends.locmem.LocMemCache", } } cache.clear() yield @pytest.fixture def totp_validation_bypass(): @contextmanager def f(): with patch("allauth.mfa.totp.internal.auth.validate_totp_code") as m: m.return_value = True yield return f @pytest.fixture def provider_id(): return "unittest-server" @pytest.fixture def openid_connect_provider_id(): return "unittest-server" @pytest.fixture def password_reset_key_generator(): def f(user): from allauth.account import app_settings token_generator = app_settings.PASSWORD_RESET_TOKEN_GENERATOR() uid = user_pk_to_url_str(user) temp_key = token_generator.make_token(user) key = f"{uid}-{temp_key}" return key return f @pytest.fixture def google_provider_settings(settings): gsettings = {"APPS": [{"client_id": "client_id", "secret": "secret"}]} settings.SOCIALACCOUNT_PROVIDERS = {"google": gsettings} return gsettings @pytest.fixture def twitter_provider_settings(settings): tsettings = {"APPS": [{"client_id": "client_id", "secret": "secret"}]} settings.SOCIALACCOUNT_PROVIDERS = {"twitter": tsettings} return tsettings @pytest.fixture def user_with_totp(user): from allauth.mfa.totp.internal import auth auth.TOTP.activate(user, auth.generate_totp_secret()) return user @pytest.fixture def user_with_recovery_codes(user_with_totp): from allauth.mfa.recovery_codes.internal import auth auth.RecoveryCodes.activate(user_with_totp) return user_with_totp @pytest.fixture def passkey(user): from allauth.mfa.models import Authenticator authenticator = Authenticator.objects.create( user=user, type=Authenticator.Type.WEBAUTHN, data={ "name": "Test passkey", "passwordless": True, "credential": {}, }, ) return authenticator @pytest.fixture def user_with_passkey(user, passkey): return user @pytest.fixture def sociallogin_setup_state(): def setup(client, process=None, next_url=None, **kwargs): state_id = "123" session = client.session state = {"process": process or AuthProcess.LOGIN, **kwargs} if next_url: state["next"] = next_url states = {} states[state_id] = [state, time.time()] session[statekit.STATES_SESSION_KEY] = states session.save() return state_id return setup @pytest.fixture def request_factory(rf): class RequestFactory: def get(self, path): request = rf.get(path) SessionMiddleware(lambda request: None).process_request(request) MessageMiddleware(lambda request: None).process_request(request) return request return RequestFactory() @pytest.fixture def get_last_email_verification_code(): from allauth.account.internal.flows import email_verification_by_code def f(client, mailoutbox): code = re.search( "\n[0-9a-z-]{6,12}\n", mailoutbox[-1].body, re.I | re.DOTALL | re.MULTILINE )[0].strip() if hasattr(client, "headless_session"): session = client.headless_session() else: session = client.session assert ( session[email_verification_by_code.EMAIL_VERIFICATION_CODE_SESSION_KEY][ "code" ] == code ) return code return f @pytest.fixture def get_last_password_reset_code(): from allauth.account.internal.flows import password_reset_by_code def f(client, mailoutbox): code = re.search( "\n[0-9a-z-]{8,12}\n", mailoutbox[-1].body, re.I | re.DOTALL | re.MULTILINE )[0].strip() if hasattr(client, "headless_session"): session = client.headless_session() else: session = client.session assert ( session[password_reset_by_code.PASSWORD_RESET_VERIFICATION_SESSION_KEY][ "code" ] == code ) return code return f @pytest.fixture def settings_impacting_urls(settings): @contextmanager def f(**kv): def reload_urlconf(): clear_url_caches() for urlconf in [ settings.ROOT_URLCONF, "allauth.account.urls", "allauth.urls", "allauth.mfa.urls", "allauth.mfa.base.urls", "allauth.headless.urls", "allauth.headless.base.urls", "allauth.headless.socialaccount.urls", "allauth.headless.usersessions.urls", "allauth.headless.mfa.urls", "allauth.idp.urls", "allauth.idp.oidc.urls", ]: if urlconf in sys.modules: importlib.reload(sys.modules[urlconf]) set_urlconf(None) old_values = {} for k, v in kv.items(): if hasattr(settings, k): old_values[k] = getattr(settings, k) setattr(settings, k, v) reload_urlconf() yield for k, v in kv.items(): if k in old_values: setattr(settings, k, old_values[k]) else: delattr(settings, k) reload_urlconf() return f @pytest.fixture(autouse=True) def clear_phone_stub(): from tests.projects.common import phone_stub yield phone_stub.clear() @pytest.fixture def sms_outbox(): from tests.projects.common import phone_stub return phone_stub.sms_outbox @pytest.fixture def phone_factory(): def f(): return f"+31{random.randint(1, 10**10):010}" return f @pytest.fixture def phone(phone_factory): return phone_factory() @pytest.fixture def user_with_phone(user, phone): from allauth.account.adapter import get_adapter get_adapter().set_phone(user, phone, True) return user def pytest_ignore_collect(collection_path, config): from tests.projects.common.settings import INSTALLED_SOCIALACCOUNT_APPS if "allauth.socialaccount.providers.saml" not in INSTALLED_SOCIALACCOUNT_APPS: if ( Path(__file__).parent / "apps" / "socialaccount" / "providers" / "saml" in collection_path.parents ): return True tests_to_skip = { "tests.projects.account_only.settings": ( "headless", "mfa", "usersessions", "socialaccount", "idp", ), "tests.projects.headless_only.settings": ("idp",), } dsm = os.getenv("DJANGO_SETTINGS_MODULE") skipped_paths = tests_to_skip.get(dsm) if not skipped_paths: return False for skipped_path in skipped_paths: abs_skipped_path = Path(__file__).parent / "apps" / skipped_path if ( abs_skipped_path == collection_path or abs_skipped_path in collection_path.parents ): return True return False @pytest.fixture() def messagesoutbox(): from tests.projects.common import adapters adapters.messagesoutbox = [] yield adapters.messagesoutbox ================================================ FILE: tests/mocking.py ================================================ import json import requests from http import HTTPStatus from unittest.mock import Mock class MockedResponse: def __init__(self, status_code, content, headers=None): if headers is None: headers = {} self.status_code = status_code if isinstance(content, dict): content = json.dumps(content) headers["content-type"] = "application/json" self.content = content.encode("utf8") self.headers = headers def json(self): return json.loads(self.text) def raise_for_status(self): pass @property def ok(self): return self.status_code // 100 == 2 @property def text(self): return self.content.decode("utf8") class mocked_response: def __init__(self, *responses, callback=None): self.callback = callback self.responses = list(responses) def __enter__(self): self.orig_get = requests.Session.get self.orig_post = requests.Session.post self.orig_request = requests.Session.request def mockable_request(f): def new_f(*args, **kwargs): if self.callback: response = self.callback(*args, **kwargs) if response is not None: return response if self.responses: resp = self.responses.pop(0) if isinstance(resp, dict): resp = MockedResponse(HTTPStatus.OK, resp) return resp return f(*args, **kwargs) return Mock(side_effect=new_f) requests.Session.get = mockable_request(requests.Session.get) requests.Session.post = mockable_request(requests.Session.post) requests.Session.request = mockable_request(requests.Session.request) def __exit__(self, type, value, traceback): requests.Session.get = self.orig_get requests.Session.post = self.orig_post requests.Session.request = self.orig_request ================================================ FILE: tests/projects/account_only/__init__.py ================================================ ================================================ FILE: tests/projects/account_only/settings.py ================================================ from pathlib import Path from django.contrib.auth.hashers import PBKDF2PasswordHasher SECRET_KEY = "psst" SITE_ID = 1 ALLOWED_HOSTS = ( "testserver", "example.com", ) USE_I18N = False USE_TZ = True DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:", "USER": "", "PASSWORD": "", "HOST": "", "PORT": "", } } ROOT_URLCONF = "tests.projects.account_only.urls" LOGIN_URL = "/accounts/login/" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [Path(__file__).parent / "templates"], "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.template.context_processors.debug", "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", ], }, }, ] CACHES = { "default": { "BACKEND": "django.core.cache.backends.dummy.DummyCache", } } MIDDLEWARE = ( "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", "allauth.account.middleware.AccountMiddleware", ) INSTALLED_APPS = ( "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.sites", "django.contrib.messages", "django.contrib.staticfiles", "django.contrib.admin", "django.contrib.humanize", "allauth", "allauth.account", ) AUTHENTICATION_BACKENDS = ( "django.contrib.auth.backends.ModelBackend", "allauth.account.auth_backends.AuthenticationBackend", ) STATIC_ROOT = "/tmp/" # Dummy STATIC_URL = "/static/" class MyPBKDF2PasswordHasher(PBKDF2PasswordHasher): """ A subclass of PBKDF2PasswordHasher that uses 1 iteration. This is for test purposes only. Never use anywhere else. """ iterations = 1 PASSWORD_HASHERS = [ "tests.projects.headless_only.settings.MyPBKDF2PasswordHasher", "django.contrib.auth.hashers.PBKDF2PasswordHasher", "django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher", "django.contrib.auth.hashers.Argon2PasswordHasher", "django.contrib.auth.hashers.BCryptSHA256PasswordHasher", ] ACCOUNT_LOGIN_BY_CODE_ENABLED = True ACCOUNT_ADAPTER = "tests.projects.common.adapters.AccountAdapter" ================================================ FILE: tests/projects/account_only/templates/429.html ================================================ 429 ================================================ FILE: tests/projects/account_only/urls.py ================================================ from django.contrib import admin from django.urls import include, path from allauth.account.decorators import secure_admin_login admin.autodiscover() admin.site.login = secure_admin_login(admin.site.login) urlpatterns = [ path("admin/", admin.site.urls), path("accounts/", include("allauth.urls")), path("", include("tests.projects.common.account.urls")), ] ================================================ FILE: tests/projects/common/__init__.py ================================================ ================================================ FILE: tests/projects/common/account/__init__.py ================================================ ================================================ FILE: tests/projects/common/account/urls.py ================================================ from django.urls import path from tests.projects.common.account.views import check_verified_email urlpatterns = [ path( "check-verified-email/", check_verified_email, name="tests_account_check_verified_email", ), ] ================================================ FILE: tests/projects/common/account/views.py ================================================ from django.http import HttpResponse from allauth.account.decorators import verified_email_required @verified_email_required def check_verified_email(request): return HttpResponse("VERIFIED") ================================================ FILE: tests/projects/common/adapters.py ================================================ import typing from django.contrib.auth import get_user_model from django.core.exceptions import ValidationError from allauth.account.adapter import DefaultAccountAdapter from tests.projects.common import phone_stub messagesoutbox = [] class AccountAdapter(DefaultAccountAdapter): def set_phone(self, user, phone: str, verified: bool): phone_stub.set_phone(user.pk, phone, verified) def get_phone(self, user) -> tuple[str, bool] | None: return phone_stub.get_phone(user.pk) def set_phone_verified(self, user, phone: str): phone_stub.set_phone(user.pk, phone, True) def get_user_by_phone(self, phone): user_id = phone_stub.get_user_id_by_phone(phone) if user_id is None: return None User = get_user_model() return User.objects.filter(pk=user_id).first() def send_verification_code_sms(self, user, phone: str, code: str, **kwargs): phone_stub.send_verification_code_sms(user, phone, code) def send_unknown_account_sms(self, phone: str, **kwargs: typing.Any) -> None: phone_stub.send_unknown_account_sms(phone) def send_account_already_exists_sms(self, phone: str, **kwargs: typing.Any) -> None: phone_stub.send_account_already_exists_sms(phone) def add_message(self, *args, **kwargs): message_template = kwargs.get("message_template") message = None if message_template is None: message = kwargs.get("message") if message is None: message_template = args[2] messagesoutbox.append(dict(message=message, message_template=message_template)) return super().add_message(*args, **kwargs) def clean_email(self, email): if email == "invalid@test.email": raise ValidationError("testing") return email ================================================ FILE: tests/projects/common/headless/__init__.py ================================================ ================================================ FILE: tests/projects/common/headless/ninja/__init__.py ================================================ ================================================ FILE: tests/projects/common/headless/ninja/urls.py ================================================ from django.urls import path from .views import api urlpatterns = [ path("", api.urls), ] ================================================ FILE: tests/projects/common/headless/ninja/views.py ================================================ from ninja import NinjaAPI from allauth.headless.contrib.ninja.security import jwt_token_auth api = NinjaAPI(urls_namespace="headless") @api.get("/resource", auth=[jwt_token_auth]) def resource(request): data = {"resource": "ok"} if "userinfo" in request.GET: data["user_email"] = request.user.email return data ================================================ FILE: tests/projects/common/headless/rest_framework/__init__.py ================================================ ================================================ FILE: tests/projects/common/headless/rest_framework/urls.py ================================================ from django.urls import path from .views import ResourceView urlpatterns = [ path("resource", ResourceView.as_view(), name="headless_rest_framework_resource"), ] ================================================ FILE: tests/projects/common/headless/rest_framework/views.py ================================================ from rest_framework.response import Response from rest_framework.views import APIView from allauth.headless.contrib.rest_framework.authentication import ( JWTTokenAuthentication, ) class ResourceView(APIView): authentication_classes = [JWTTokenAuthentication] def get(self, request, *args, **kwargs): data = {"resource": "ok"} if "userinfo" in request.GET: data["user_email"] = request.user.email return Response(data) ================================================ FILE: tests/projects/common/headless/urls.py ================================================ from django.urls import include, path urlpatterns = [ path("drf/", include("tests.projects.common.headless.rest_framework.urls")), path("ninja/", include("tests.projects.common.headless.ninja.urls")), ] ================================================ FILE: tests/projects/common/idp/__init__.py ================================================ ================================================ FILE: tests/projects/common/idp/ninja/__init__.py ================================================ ================================================ FILE: tests/projects/common/idp/ninja/urls.py ================================================ from django.urls import path from .views import api urlpatterns = [ path("", api.urls), ] ================================================ FILE: tests/projects/common/idp/ninja/views.py ================================================ from ninja import NinjaAPI from allauth.idp.oidc.contrib.ninja.security import TokenAuth api = NinjaAPI() @api.get("/resource", auth=[TokenAuth(scope=["view-resource"])]) def resource(request): return {"resource": "ok", "user_email": request.user.email} ================================================ FILE: tests/projects/common/idp/rest_framework/__init__.py ================================================ ================================================ FILE: tests/projects/common/idp/rest_framework/urls.py ================================================ from django.urls import path from .views import ResourceView urlpatterns = [ path("resource", ResourceView.as_view(), name="idp_rest_framework_resource"), ] ================================================ FILE: tests/projects/common/idp/rest_framework/views.py ================================================ from rest_framework.response import Response from rest_framework.views import APIView from allauth.idp.oidc.contrib.rest_framework.authentication import TokenAuthentication from allauth.idp.oidc.contrib.rest_framework.permissions import TokenPermission class ResourceView(APIView): authentication_classes = [TokenAuthentication] permission_classes = [TokenPermission.has_scope(["view-resource"])] def get(self, request, *args, **kwargs): return Response( { "resource": "ok", "user_email": ( request.user.email if request.user and request.user.is_authenticated else None ), } ) ================================================ FILE: tests/projects/common/idp/urls.py ================================================ from django.urls import include, path urlpatterns = [ path("drf/", include("tests.projects.common.idp.rest_framework.urls")), path("ninja/", include("tests.projects.common.idp.ninja.urls")), ] ================================================ FILE: tests/projects/common/phone_stub.py ================================================ import typing from django.db.utils import IntegrityError db: dict[int, tuple[str, bool]] = {} sms_outbox: list[dict] = [] def clear(): db.clear() sms_outbox.clear() def set_phone(user_id, phone: str, verified: bool): for other_user_id, value in db.items(): if user_id != other_user_id and value[0] == phone: raise IntegrityError db[user_id] = (phone, verified) def get_phone(user_id) -> tuple[str, bool] | None: return db.get(user_id) def send_verification_code_sms(user, phone: str, code: str): sms_outbox.append( { "user_id": user.pk, "phone": phone, "code": code, } ) def send_unknown_account_sms(phone: str, **kwargs: typing.Any): sms_outbox.append({"phone": phone, "reason": "unknon"}) def send_account_already_exists_sms(phone: str, **kwargs: typing.Any): sms_outbox.append({"phone": phone, "reason": "exists"}) def get_user_id_by_phone(phone): for k, v in db.items(): if v[0] == phone: return k return None ================================================ FILE: tests/projects/common/settings.py ================================================ from pathlib import Path INSTALLED_SOCIALACCOUNT_APPS: tuple[str, ...] = ( "allauth.socialaccount", "allauth.socialaccount.providers.agave", "allauth.socialaccount.providers.amazon", "allauth.socialaccount.providers.amazon_cognito", "allauth.socialaccount.providers.angellist", "allauth.socialaccount.providers.apple", "allauth.socialaccount.providers.asana", "allauth.socialaccount.providers.atlassian", "allauth.socialaccount.providers.auth0", "allauth.socialaccount.providers.authentiq", "allauth.socialaccount.providers.baidu", "allauth.socialaccount.providers.basecamp", "allauth.socialaccount.providers.battlenet", "allauth.socialaccount.providers.bitbucket_oauth2", "allauth.socialaccount.providers.bitly", "allauth.socialaccount.providers.box", "allauth.socialaccount.providers.cilogon", "allauth.socialaccount.providers.clever", "allauth.socialaccount.providers.coinbase", "allauth.socialaccount.providers.dataporten", "allauth.socialaccount.providers.daum", "allauth.socialaccount.providers.digitalocean", "allauth.socialaccount.providers.dingtalk", "allauth.socialaccount.providers.discogs", "allauth.socialaccount.providers.discord", "allauth.socialaccount.providers.disqus", "allauth.socialaccount.providers.douban", "allauth.socialaccount.providers.doximity", "allauth.socialaccount.providers.draugiem", "allauth.socialaccount.providers.drip", "allauth.socialaccount.providers.dropbox", "allauth.socialaccount.providers.dummy", "allauth.socialaccount.providers.dwolla", "allauth.socialaccount.providers.edmodo", "allauth.socialaccount.providers.edx", "allauth.socialaccount.providers.eventbrite", "allauth.socialaccount.providers.eveonline", "allauth.socialaccount.providers.evernote", "allauth.socialaccount.providers.exist", "allauth.socialaccount.providers.facebook", "allauth.socialaccount.providers.feedly", "allauth.socialaccount.providers.feishu", "allauth.socialaccount.providers.figma", "allauth.socialaccount.providers.fivehundredpx", "allauth.socialaccount.providers.flickr", "allauth.socialaccount.providers.foursquare", "allauth.socialaccount.providers.frontier", "allauth.socialaccount.providers.fxa", "allauth.socialaccount.providers.gitea", "allauth.socialaccount.providers.github", "allauth.socialaccount.providers.gitlab", "allauth.socialaccount.providers.globus", "allauth.socialaccount.providers.google", "allauth.socialaccount.providers.gumroad", "allauth.socialaccount.providers.hubic", "allauth.socialaccount.providers.hubspot", "allauth.socialaccount.providers.instagram", "allauth.socialaccount.providers.jupyterhub", "allauth.socialaccount.providers.kakao", "allauth.socialaccount.providers.lemonldap", "allauth.socialaccount.providers.lichess", "allauth.socialaccount.providers.line", "allauth.socialaccount.providers.linkedin_oauth2", "allauth.socialaccount.providers.mailchimp", "allauth.socialaccount.providers.mailcow", "allauth.socialaccount.providers.mailru", "allauth.socialaccount.providers.mediawiki", "allauth.socialaccount.providers.meetup", "allauth.socialaccount.providers.microsoft", "allauth.socialaccount.providers.miro", "allauth.socialaccount.providers.naver", "allauth.socialaccount.providers.netiq", "allauth.socialaccount.providers.nextcloud", "allauth.socialaccount.providers.notion", "allauth.socialaccount.providers.odnoklassniki", "allauth.socialaccount.providers.okta", "allauth.socialaccount.providers.openid", "allauth.socialaccount.providers.openid_connect", "allauth.socialaccount.providers.openstreetmap", "allauth.socialaccount.providers.orcid", "allauth.socialaccount.providers.patreon", "allauth.socialaccount.providers.paypal", "allauth.socialaccount.providers.pinterest", "allauth.socialaccount.providers.pocket", "allauth.socialaccount.providers.questrade", "allauth.socialaccount.providers.quickbooks", "allauth.socialaccount.providers.reddit", "allauth.socialaccount.providers.robinhood", "allauth.socialaccount.providers.salesforce", "allauth.socialaccount.providers.sharefile", "allauth.socialaccount.providers.shopify", "allauth.socialaccount.providers.slack", "allauth.socialaccount.providers.snapchat", "allauth.socialaccount.providers.soundcloud", "allauth.socialaccount.providers.spotify", "allauth.socialaccount.providers.stackexchange", "allauth.socialaccount.providers.steam", "allauth.socialaccount.providers.stocktwits", "allauth.socialaccount.providers.strava", "allauth.socialaccount.providers.stripe", "allauth.socialaccount.providers.telegram", "allauth.socialaccount.providers.tiktok", "allauth.socialaccount.providers.trainingpeaks", "allauth.socialaccount.providers.trello", "allauth.socialaccount.providers.tumblr", "allauth.socialaccount.providers.tumblr_oauth2", "allauth.socialaccount.providers.twentythreeandme", "allauth.socialaccount.providers.twitch", "allauth.socialaccount.providers.twitter", "allauth.socialaccount.providers.twitter_oauth2", "allauth.socialaccount.providers.untappd", "allauth.socialaccount.providers.vimeo", "allauth.socialaccount.providers.vimeo_oauth2", "allauth.socialaccount.providers.vk", "allauth.socialaccount.providers.wahoo", "allauth.socialaccount.providers.weibo", "allauth.socialaccount.providers.weixin", "allauth.socialaccount.providers.windowslive", "allauth.socialaccount.providers.xing", "allauth.socialaccount.providers.yahoo", "allauth.socialaccount.providers.yandex", "allauth.socialaccount.providers.ynab", "allauth.socialaccount.providers.zoho", "allauth.socialaccount.providers.zoom", ) # Instaling the necessary dependencies for testing SAML slows the CI pipeline # down considerably, as we need to: # # pip install --no-binary xmlsec --no-binary lxml {opts} {packages} # # Also see: https://github.com/xmlsec/python-xmlsec/issues/320#issuecomment-2129076807 try: import onelogin # noqa INSTALLED_SOCIALACCOUNT_APPS += ("allauth.socialaccount.providers.saml",) except ImportError: pass IDP_OIDC_PRIVATE_KEY = """ -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDAvGhvhFJkUwEa jq48SQ2rWHxPC/kbG9lJb0cl6qjaLA27atC/aCdmnlMnnng7PrjfOLtPatXjuKk3 S8wpIqB74wuzEnBs+/3BaqylxN1pkazRv+w0i26B7igLQVjsUXkz/icSPoSkZSxO MB4vbGfwnbyUOqHaWumnDwXOZ4FKrxxXMSJcJVF141MaqANh90Wwsx7PK/Nb8hkj blKcbfSn9x5Q7jRN2e1yfC6xtsrR+q5o26m0H2uXiFaWtxH6GPKnsgA90RmIblQ7 nYmg39C01Xli0z5ehzd5oAGpXEDP9uO9+1kVWor8TETl7vpaVqnauLx/tTl09qEM WpX6VmFVAgMBAAECggEAEdPbnSUSMfFzkq9L8ouXVhgTN4SWACntSVufqyQvhi64 /nL86BeMPXO7oViJKoG8u/kVal0pd6znChRayBtJ2OvBc0jrWUldyXxCh/rTuCYf ZC9qe9nB2QbccV4UCZfnrCWAG7HotwQcuwa8ZAqU+q68eMGLoxTxs+Ax20u7q9qp Y+QNNZOuH+1pC8l+0CaTTpFa1sty+/xnGtM6UgaLVQ7E4ZvFRWyWfIAzWFzFfFWW oVK7mYD8uVPMEpHPPlaNj+0C7LNM1NUpg/ifKPYl13OnhQr2WMji35LwrEsK0lpr tXw6wm7rl9NbXC93hpW9V252KNuL3LbN9lLAICSOAQKBgQD24BY4dCRErTZY8+Zp fBf+2h6RPHtukTFsyMdC903R4ZuLGSoteKTesAte2CfsG5YvgXx/eZHGRvnPBF7A Pjb/ZJnk3HzsHirivZ19xvd5MVQRobUXmh42gJW5V5XixqMjBfpoWcOL5H1ly9m+ 2EZKeh7B8wNXTj/dCoOmNbF3QQKBgQDH3A8gkWT6udSMTVT1oxb1ifJUTWtwdfrn rZQtk1ov962sXwMqglt9sdt9+9gajF7LzxLxStRUY16U0QUMjfHVQiQQfzfnI2a7 r2dg1g03msDg7QRVD/NHuzkQL37zeQQalrqTCbq43/CFbJDlGkr9xqNfAICya4Vp gJW2zdJZFQKBgQCcS4Rl20m23P5iVI+USscaRtdBVcxDVNK4r2hPwifXb4C9EIJ+ ZTnj7gpU0n574X80tkKupbWflQHEiVy/UuQYzoULunewOO0nvan+nj/Az3UM8Jao yZ7FHKUtwQCYoO9ZVgiRlfrSDydAkk1ZoKznq+bbHVIJLPYLqANu7+FZwQKBgQCn Oq35rU7WMGn137soMgfC+mMnQQSWPFHuSyKCpBpBqrfKVFH83siZOxoSp4kiZbPo S2NpPRi/Z8o7MU5NO/RPYiF1IE3xfIC4qMMSlujGTxn22rvWRRtmOPU9YtCR/v99 FAQXhnuTt+W0bqwq1z5KbExE8NG++RLPvYUISd4pJQKBgGxyMWgE2AeILIcrw/Ts zM1ct2vV7Iet8eVRPUAzESQu0aGBm7Eho9+mh3vUJtlStChJIvCT+lbEgXmsDGMk HxD4lATnNILRfRTdPgu8IYS3/A4LoXjjhsPmx8NQ6PwnKnseFtQMBKQsX2HVfMVP vOZ+KpqzUW/vig+SalRbQMIR -----END PRIVATE KEY----- """ HEADLESS_JWT_PRIVATE_KEY = IDP_OIDC_PRIVATE_KEY TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [Path(__file__).parent / "templates"], "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.template.context_processors.debug", "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", ], }, }, ] ================================================ FILE: tests/projects/common/templates/test_403_csrf.html ================================================ {% load socialaccount %} Sign In ================================================ FILE: tests/projects/common/urls.py ================================================ from django.urls import include, path urlpatterns = [ path("idp/", include("tests.projects.common.idp.urls")), path("headless/", include("tests.projects.common.headless.urls")), path("account/", include("tests.projects.common.account.urls")), ] ================================================ FILE: tests/projects/headless_only/__init__.py ================================================ ================================================ FILE: tests/projects/headless_only/settings.py ================================================ from pathlib import Path from django.contrib.auth.hashers import PBKDF2PasswordHasher from tests.projects.common.settings import HEADLESS_JWT_PRIVATE_KEY # noqa from tests.projects.common.settings import INSTALLED_SOCIALACCOUNT_APPS SECRET_KEY = "psst" SITE_ID = 1 ALLOWED_HOSTS = ( "testserver", "example.com", ) USE_I18N = False USE_TZ = True DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:", "USER": "", "PASSWORD": "", "HOST": "", "PORT": "", } } ROOT_URLCONF = "tests.projects.headless_only.urls" LOGIN_URL = "/login/" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [ Path(__file__).parent.parent / "examples" / "regular-django" / "example" / "templates" ], "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.template.context_processors.debug", "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", ], }, }, ] CACHES = { "default": { "BACKEND": "django.core.cache.backends.dummy.DummyCache", } } MIDDLEWARE = ( "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", "allauth.account.middleware.AccountMiddleware", ) INSTALLED_APPS = ( "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.sites", "django.contrib.messages", "django.contrib.staticfiles", "django.contrib.admin", "django.contrib.humanize", "allauth", "allauth.account", "allauth.mfa", "allauth.usersessions", "allauth.headless", ) + INSTALLED_SOCIALACCOUNT_APPS AUTHENTICATION_BACKENDS = ( "django.contrib.auth.backends.ModelBackend", "allauth.account.auth_backends.AuthenticationBackend", ) STATIC_ROOT = "/tmp/" # Dummy STATIC_URL = "/static/" class MyPBKDF2PasswordHasher(PBKDF2PasswordHasher): """ A subclass of PBKDF2PasswordHasher that uses 1 iteration. This is for test purposes only. Never use anywhere else. """ iterations = 1 PASSWORD_HASHERS = [ "tests.projects.headless_only.settings.MyPBKDF2PasswordHasher", "django.contrib.auth.hashers.PBKDF2PasswordHasher", "django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher", "django.contrib.auth.hashers.Argon2PasswordHasher", "django.contrib.auth.hashers.BCryptSHA256PasswordHasher", ] SOCIALACCOUNT_QUERY_EMAIL = True SOCIALACCOUNT_PROVIDERS = { "openid_connect": { "APPS": [ { "provider_id": "unittest-server", "name": "Unittest Server", "client_id": "Unittest client_id", "client_secret": "Unittest client_secret", "settings": { "server_url": "https://unittest.example.com", }, }, { "provider_id": "other-server", "name": "Other Example Server", "client_id": "other client_id", "client_secret": "other client_secret", "settings": { "server_url": "https://other.example.com", }, }, ], } } ACCOUNT_LOGIN_BY_CODE_ENABLED = True ACCOUNT_ADAPTER = "tests.projects.common.adapters.AccountAdapter" HEADLESS_ONLY = True HEADLESS_FRONTEND_URLS = { "account_confirm_email": "/spa/confirm-email?key={key}", "account_reset_password": "/spa/password/reset/", "account_reset_password_from_key": "/spa/password/reset/{key}/", "account_signup": "/spa/signup", "socialaccount_login_error": "/spa/error", } HEADLESS_SERVE_SPECIFICATION = True MFA_SUPPORTED_TYPES = ["totp", "webauthn", "recovery_codes"] MFA_PASSKEY_LOGIN_ENABLED = True MFA_PASSKEY_SIGNUP_ENABLED = True ================================================ FILE: tests/projects/headless_only/urls.py ================================================ from django.urls import include, path urlpatterns = [ path("accounts/", include("allauth.urls")), path("_allauth/", include("allauth.headless.urls")), path("", include("tests.projects.common.urls")), ] ================================================ FILE: tests/projects/login_required_mw/__init__.py ================================================ ================================================ FILE: tests/projects/login_required_mw/middleware.py ================================================ from django.contrib.auth.middleware import LoginRequiredMiddleware class CustomLoginRequiredMiddleware(LoginRequiredMiddleware): def handle_no_permission(self, request, view_func): # Quick & dirty workaround for: https://github.com/vitalik/django-ninja/issues/1461 if "/ninja/" in request.path: return None return super().handle_no_permission(request, view_func) ================================================ FILE: tests/projects/login_required_mw/settings.py ================================================ from django.contrib.auth.hashers import PBKDF2PasswordHasher from tests.projects.common.settings import HEADLESS_JWT_PRIVATE_KEY # noqa from tests.projects.common.settings import IDP_OIDC_PRIVATE_KEY # noqa from tests.projects.common.settings import TEMPLATES # noqa from tests.projects.common.settings import INSTALLED_SOCIALACCOUNT_APPS SECRET_KEY = "psst" SITE_ID = 1 ALLOWED_HOSTS = ( "testserver", "example.com", ) USE_I18N = False USE_TZ = True DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:", "USER": "", "PASSWORD": "", "HOST": "", "PORT": "", } } ROOT_URLCONF = "tests.projects.login_required_mw.urls" LOGIN_URL = "/accounts/login/" CACHES = { "default": { "BACKEND": "django.core.cache.backends.dummy.DummyCache", } } MIDDLEWARE = ( "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "tests.projects.login_required_mw.middleware.CustomLoginRequiredMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", "allauth.account.middleware.AccountMiddleware", ) INSTALLED_APPS = ( "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.sites", "django.contrib.messages", "django.contrib.staticfiles", "django.contrib.admin", "django.contrib.humanize", "allauth", "allauth.account", "allauth.mfa", "allauth.usersessions", "allauth.headless", "allauth.idp.oidc", ) + INSTALLED_SOCIALACCOUNT_APPS AUTHENTICATION_BACKENDS = ( "django.contrib.auth.backends.ModelBackend", "allauth.account.auth_backends.AuthenticationBackend", ) STATIC_ROOT = "/tmp/" # Dummy STATIC_URL = "/static/" class MyPBKDF2PasswordHasher(PBKDF2PasswordHasher): """ A subclass of PBKDF2PasswordHasher that uses 1 iteration. This is for test purposes only. Never use anywhere else. """ iterations = 1 PASSWORD_HASHERS = [ "tests.projects.login_required_mw.settings.MyPBKDF2PasswordHasher", "django.contrib.auth.hashers.PBKDF2PasswordHasher", "django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher", "django.contrib.auth.hashers.Argon2PasswordHasher", "django.contrib.auth.hashers.BCryptSHA256PasswordHasher", ] SOCIALACCOUNT_QUERY_EMAIL = True SOCIALACCOUNT_PROVIDERS = { "openid_connect": { "APPS": [ { "provider_id": "unittest-server", "name": "Unittest Server", "client_id": "Unittest client_id", "client_secret": "Unittest client_secret", "settings": { "server_url": "https://unittest.example.com", }, }, { "provider_id": "other-server", "name": "Other Example Server", "client_id": "other client_id", "client_secret": "other client_secret", "settings": { "server_url": "https://other.example.com", }, }, ], } } ACCOUNT_LOGIN_BY_CODE_ENABLED = True ACCOUNT_ADAPTER = "tests.projects.common.adapters.AccountAdapter" MFA_SUPPORTED_TYPES = ["totp", "webauthn", "recovery_codes"] MFA_PASSKEY_LOGIN_ENABLED = True MFA_PASSKEY_SIGNUP_ENABLED = True HEADLESS_SERVE_SPECIFICATION = True ================================================ FILE: tests/projects/login_required_mw/templates/429.html ================================================ 429 ================================================ FILE: tests/projects/login_required_mw/urls.py ================================================ from django.contrib import admin from django.urls import include, path from allauth.account.decorators import secure_admin_login admin.autodiscover() admin.site.login = secure_admin_login(admin.site.login) urlpatterns = [ path("admin/", admin.site.urls), path("accounts/", include("allauth.urls")), path("_allauth/", include("allauth.headless.urls")), path("", include("allauth.idp.urls")), path("", include("tests.projects.common.urls")), ] ================================================ FILE: tests/projects/regular/__init__.py ================================================ ================================================ FILE: tests/projects/regular/settings.py ================================================ from django.contrib.auth.hashers import PBKDF2PasswordHasher from tests.projects.common.settings import HEADLESS_JWT_PRIVATE_KEY # noqa from tests.projects.common.settings import IDP_OIDC_PRIVATE_KEY # noqa from tests.projects.common.settings import TEMPLATES # noqa from tests.projects.common.settings import INSTALLED_SOCIALACCOUNT_APPS SECRET_KEY = "psst" SITE_ID = 1 ALLOWED_HOSTS = ( "testserver", "example.com", ) USE_I18N = False USE_TZ = True DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:", "USER": "", "PASSWORD": "", "HOST": "", "PORT": "", } } ROOT_URLCONF = "tests.projects.regular.urls" LOGIN_URL = "/accounts/login/" CACHES = { "default": { "BACKEND": "django.core.cache.backends.dummy.DummyCache", } } MIDDLEWARE = ( "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", "allauth.account.middleware.AccountMiddleware", ) INSTALLED_APPS = ( "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.sites", "django.contrib.messages", "django.contrib.staticfiles", "django.contrib.admin", "django.contrib.humanize", "allauth", "allauth.account", "allauth.mfa", "allauth.usersessions", "allauth.headless", "allauth.idp.oidc", ) + INSTALLED_SOCIALACCOUNT_APPS AUTHENTICATION_BACKENDS = ( "django.contrib.auth.backends.ModelBackend", "allauth.account.auth_backends.AuthenticationBackend", ) STATIC_ROOT = "/tmp/" # Dummy STATIC_URL = "/static/" class MyPBKDF2PasswordHasher(PBKDF2PasswordHasher): """ A subclass of PBKDF2PasswordHasher that uses 1 iteration. This is for test purposes only. Never use anywhere else. """ iterations = 1 PASSWORD_HASHERS = [ "tests.projects.regular.settings.MyPBKDF2PasswordHasher", "django.contrib.auth.hashers.PBKDF2PasswordHasher", "django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher", "django.contrib.auth.hashers.Argon2PasswordHasher", "django.contrib.auth.hashers.BCryptSHA256PasswordHasher", ] ACCOUNT_ADAPTER = "tests.projects.common.adapters.AccountAdapter" SOCIALACCOUNT_QUERY_EMAIL = True SOCIALACCOUNT_PROVIDERS = { "openid_connect": { "APPS": [ { "provider_id": "unittest-server", "name": "Unittest Server", "client_id": "Unittest client_id", "client_secret": "Unittest client_secret", "settings": { "server_url": "https://unittest.example.com", }, }, { "provider_id": "other-server", "name": "Other Example Server", "client_id": "other client_id", "client_secret": "other client_secret", "settings": { "server_url": "https://other.example.com", }, }, ], } } ACCOUNT_LOGIN_BY_CODE_ENABLED = True MFA_SUPPORTED_TYPES = ["totp", "webauthn", "recovery_codes"] MFA_PASSKEY_LOGIN_ENABLED = True MFA_PASSKEY_SIGNUP_ENABLED = True HEADLESS_SERVE_SPECIFICATION = True ================================================ FILE: tests/projects/regular/urls.py ================================================ from django.contrib import admin from django.urls import include, path from allauth.account.decorators import secure_admin_login admin.autodiscover() admin.site.login = secure_admin_login(admin.site.login) urlpatterns = [ path("admin/", admin.site.urls), path("accounts/", include("allauth.urls")), path("_allauth/", include("allauth.headless.urls")), path("", include("allauth.idp.urls")), path("", include("tests.projects.common.urls")), ]