├── .github ├── FUNDING.yml ├── SECURITY.md └── workflows │ ├── ci.yaml │ └── release.yaml ├── .gitignore ├── .markdownlint.json ├── .pre-commit-config.yaml ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── django_zxcvbn_password_validator ├── __init__.py ├── locale │ ├── ar │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── cs │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── fr │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── hu │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── id │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── nl │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ └── pt_BR │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── settings.py ├── test_translate_zxcvbn_text.py ├── test_zxcvbn_password_validator.py ├── translate_zxcvbn_text.py └── zxcvbn_password_validator.py ├── doc ├── english_example.png └── french_example.png ├── example_project ├── .gitignore ├── example │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── manage.py └── requirements.txt ├── manage.py ├── pyproject.toml └── translation_project ├── __init__.py ├── management ├── __init__.py └── commands │ ├── __init__.py │ └── generatei18ndict.py ├── settings.py ├── urls.py └── wsgi.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [Pierre-Sassoulas] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: "pypi/django-zxcvbn-password-validator" 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: Pierre-Sassoulas 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Coordinated Disclosure Plan 2 | 3 | [Coordinated Disclosure Plan](https://tidelift.com/security) 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: ~ 8 | 9 | env: 10 | CACHE_VERSION: 4 11 | DEFAULT_PYTHON: "3.12" 12 | PRE_COMMIT_CACHE: ~/.cache/pre-commit 13 | 14 | jobs: 15 | tests: 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 20 | django-version: ["<4", "<5", "<6"] 21 | exclude: 22 | - python-version: "3.13" 23 | django-version: "<4" 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: Python-${{ matrix.python-version }}-django${{ matrix.django-version }} 27 | uses: actions/setup-python@v2 28 | with: 29 | python-version: ${{ matrix.python-version }} 30 | - name: Install dependencies 31 | run: | 32 | sudo apt-get install -yqq gettext 33 | pip3 install -e ".[dev]" 34 | pip3 install "django${{ matrix.django-version }}" 35 | - name: Test 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | run: | 39 | python manage.py compilemessages 40 | coverage run --source=django_zxcvbn_password_validator ./manage.py test 41 | coverage html 42 | coveralls debug --service=github 43 | 44 | lint: 45 | runs-on: ubuntu-latest 46 | strategy: 47 | matrix: 48 | python-version: ["3.12"] 49 | django-version: ["<4", "<5", "<6"] 50 | steps: 51 | - uses: actions/checkout@v2 52 | - name: Python-${{ matrix.python-version }}-django${{ matrix.django-version }} 53 | uses: actions/setup-python@v2 54 | with: 55 | python-version: ${{ matrix.python-version }} 56 | - name: Install dependencies 57 | run: | 58 | sudo apt-get install -yqq gettext 59 | pip3 install -e ".[pylint]" 60 | pip3 install "django${{ matrix.django-version }}" 61 | - name: Lint 62 | run: | 63 | pre-commit run pylint --all-files 64 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | env: 9 | DEFAULT_PYTHON: "3.11" 10 | 11 | jobs: 12 | release-pypi: 13 | name: Upload release to PyPI 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Check out code from Github 17 | uses: actions/checkout@v4.1.1 18 | - name: Set up Python ${{ env.DEFAULT_PYTHON }} 19 | id: python 20 | uses: actions/setup-python@v5.0.0 21 | with: 22 | python-version: ${{ env.DEFAULT_PYTHON }} 23 | check-latest: true 24 | - name: Install requirements 25 | run: | 26 | # Remove dist, build, and *.egg-info 27 | # when building locally for testing! 28 | python -m pip install twine build 29 | - name: Build distributions 30 | run: | 31 | python -m build 32 | - name: Upload to PyPI 33 | if: github.event_name == 'release' && startsWith(github.ref, 'refs/tags') 34 | env: 35 | TWINE_REPOSITORY: pypi 36 | TWINE_USERNAME: __token__ 37 | TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} 38 | run: | 39 | twine upload --verbose dist/* 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.log 3 | *.pyc 4 | .project 5 | .pydevproject 6 | build/ 7 | django_zxcvbn_password_validator.egg-info/ 8 | dist/ 9 | db.sqlite3 10 | .coverage 11 | htmlcov/ 12 | .env/ 13 | venv/ 14 | .idea/ 15 | .tox/ 16 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "first-heading-h1": false, 4 | "hr-style": false, 5 | "line-length": false, 6 | "no-duplicate-heading": false, 7 | "no-inline-html": { 8 | "allowed_elements": ["br", "sup", "sub"] 9 | }, 10 | "ol-prefix": false 11 | } 12 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | ci: 2 | skip: [pylint] 3 | autoupdate_schedule: "quarterly" 4 | 5 | repos: 6 | - repo: https://github.com/astral-sh/ruff-pre-commit 7 | rev: "v0.11.4" 8 | hooks: 9 | - id: ruff 10 | args: ["--fix"] 11 | exclude: &nocheck "translation_project/|example_project" 12 | - id: ruff-format 13 | - repo: https://github.com/asottile/blacken-docs 14 | rev: 1.19.1 15 | hooks: 16 | - id: blacken-docs 17 | additional_dependencies: [black==24.2.0] 18 | - repo: https://github.com/pre-commit/pre-commit-hooks 19 | rev: v5.0.0 20 | hooks: 21 | - id: check-merge-conflict 22 | - id: trailing-whitespace 23 | args: [--markdown-linebreak-ext=md] 24 | - id: end-of-file-fixer 25 | - id: check-json 26 | exclude: "tests/config/fixtures/album_duplicated.json" 27 | - id: check-ast 28 | - repo: https://github.com/regebro/pyroma 29 | rev: "4.2" 30 | hooks: 31 | - id: pyroma 32 | # Must be specified because of the default value in pyroma 33 | always_run: false 34 | files: | 35 | (?x)^( 36 | README.md| 37 | pyproject.toml 38 | )$ 39 | - repo: local 40 | hooks: 41 | - id: pylint 42 | name: pylint 43 | entry: pylint 44 | language: system 45 | args: ["-sn", "-rn"] 46 | types: [python] 47 | exclude: *nocheck 48 | - repo: https://github.com/pre-commit/mirrors-mypy 49 | rev: v1.15.0 50 | hooks: 51 | - id: mypy 52 | name: mypy 53 | entry: mypy 54 | language: python 55 | "types": [python] 56 | args: ["--strict"] 57 | additional_dependencies: ["django-stubs"] 58 | require_serial: true 59 | exclude: *nocheck 60 | - repo: https://github.com/tox-dev/pyproject-fmt 61 | rev: "v2.5.1" 62 | hooks: 63 | - id: pyproject-fmt 64 | - repo: https://github.com/rbubley/mirrors-prettier 65 | rev: v3.5.3 66 | hooks: 67 | - id: prettier 68 | args: [--prose-wrap=always, --print-width=88] 69 | exclude: "tests/config/fixtures/album_duplicated.yaml" 70 | - repo: https://github.com/PyCQA/bandit 71 | rev: 1.8.3 72 | hooks: 73 | - id: bandit 74 | args: ["-r", "-lll"] 75 | exclude: *nocheck 76 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Translating the project 4 | 5 | This project is available in multiple language. Your contribution would be very 6 | appreciated if you know a language that is not yet available. 7 | 8 | The translations can be edited online using 9 | [Weblate](https://hosted.weblate.org/engage/django-zxcvbn-password-validator/). 10 | 11 | ### I18n 12 | 13 | ```bash 14 | python manage.py makemessages --locale= 15 | # python manage.py migrate 16 | # python manage.py createsuperuser ? (You need to login for rosetta) 17 | python manage.py runserver 18 | # Access http://localhost:8000/admin to login 19 | # Then go to http://localhost:8000/rosetta to translate 20 | python manage.py makemessages --no-obsolete --no-wrap 21 | ``` 22 | 23 | ## Testing 24 | 25 | ```bash 26 | python manage.py test 27 | ``` 28 | 29 | ## Coverage 30 | 31 | ```bash 32 | coverage run ./manage.py test 33 | coverage html 34 | # Open htmlcov/index.html in a navigator 35 | ``` 36 | 37 | ## Lint 38 | 39 | We're using `pre-commit`, it should take care of linting during commit. 40 | 41 | ```bash 42 | pip3 install -e ".[dev]" 43 | pre-commit install 44 | ``` 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Pierre Sassoulas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include django_zxcvbn_password_validator/locale *.po *.mo 2 | global-exclude __pycache__ 3 | global-exclude *.py[co] 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # django-zxcvbn-password-validator 2 | 3 | A translatable password validator for django, based on zxcvbn-python and available with 4 | pip. 5 | 6 | Professional support for django-zxcvbn-password-validator is available as part of the 7 | [Tidelift Subscription](https://tidelift.com/subscription/pkg/pypi-django-zxcvbn-password-validator?utm_source=pypi-django-zxcvbn-password-validator&utm_medium=referral&utm_campaign=enterprise) 8 | 9 | [![Build Status](https://travis-ci.org/Pierre-Sassoulas/django-zxcvbn-password-validator.svg?branch=master)](https://travis-ci.org/Pierre-Sassoulas/django-zxcvbn-password-validator) 10 | [![Coverage Status](https://coveralls.io/repos/github/Pierre-Sassoulas/django-zxcvbn-password-validator/badge.svg?branch=master)](https://coveralls.io/github/Pierre-Sassoulas/django-zxcvbn-password-validator?branch=master) 11 | [![PyPI version](https://badge.fury.io/py/django-zxcvbn-password-validator.svg)](https://badge.fury.io/py/django-zxcvbn-password-validator) 12 | 13 | ## Translating the project 14 | 15 | This project is available in multiple language. Your contribution would be very 16 | appreciated if you know a language that is not yet available. See 17 | [how to contribute](CONTRIBUTING.md) 18 | 19 | ### Language available 20 | 21 | The software is developed in English. Other available languages are: 22 | 23 | [![Translation status](https://hosted.weblate.org/widget/django-zxcvbn-password-validator/multi-auto.svg)](https://hosted.weblate.org/engage/django-zxcvbn-password-validator/) 24 | 25 | - [x] Dutch thanks to [Thom Wiggers](https://github.com/thomwiggers/) 26 | - [x] French thanks to [Pierre Sassoulas](https://github.com/Pierre-Sassoulas/) and 27 | [Lionel Sausin](https://github.com/ls-initiatives) 28 | - [x] Brazilian Portuguese thanks to [Andrés Martano](https://github.com/andresmrm/) 29 | - [x] Czech thanks to [Michal Čihař](https://github.com/nijel/) 30 | - [x] English 31 | 32 | ## Creating a user with django-zxcvbn-password-validator 33 | 34 | If the password is not strong enough, we provide errors explaining what you need to do : 35 | 36 | ![English example](doc/english_example.png "English example") 37 | 38 | The error message are translated to your target language (even the string given by 39 | zxcvbn that are in english only) : 40 | 41 | ![Translated example](doc/french_example.png "Translated example") 42 | 43 | ## How to use 44 | 45 | Add `django-zxcvbn-password-validator` to your requirements and get it with pip. Then 46 | everything happens in your settings file. 47 | 48 | Add `'django_zxcvbn_password_validator'` in the `INSTALLED_APPS` : 49 | 50 | ```python 51 | INSTALLED_APPS = [ 52 | # ... 53 | "django_zxcvbn_password_validator" 54 | ] 55 | ``` 56 | 57 | Modify `AUTH_PASSWORD_VALIDATORS` : 58 | 59 | ```python 60 | AUTH_PASSWORD_VALIDATORS = [ 61 | { 62 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", 63 | }, 64 | { 65 | "NAME": "django_zxcvbn_password_validator.ZxcvbnPasswordValidator", 66 | }, 67 | # ... 68 | ] 69 | ``` 70 | 71 | You could choose to use zxcvbn alone, but I personally still use Django's 72 | `UserAttributeSimilarityValidator`, because there seems to be still be some problem with 73 | it integrating user information with zxcvbn (as of june 2018). 74 | 75 | Finally, you can set the `PASSWORD_MINIMAL_STRENGTH` to your liking (default is 2), 76 | every password scoring lower than this number will be rejected : 77 | 78 | ```python 79 | # 0 too guessable: risky password. (guesses < 10^3) 80 | # 1 very guessable: protection from throttled online attacks. 81 | # (guesses < 10^6) 82 | # 2 somewhat guessable: protection from unthrottled online attacks. 83 | # (guesses < 10^8) 84 | # 3 safely unguessable: moderate protection from offline slow-hash scenario. 85 | # (guesses < 10^10) 86 | # 4 very unguessable: strong protection from offline slow-hash scenario. 87 | # (guesses >= 10^10) 88 | PASSWORD_MINIMAL_STRENGTH = 0 if DEBUG else 4 89 | ``` 90 | -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/__init__.py: -------------------------------------------------------------------------------- 1 | """A password validator for django, based on zxcvbn-python.""" 2 | 3 | from django_zxcvbn_password_validator.settings import DEFAULT_MINIMAL_STRENGTH 4 | from django_zxcvbn_password_validator.zxcvbn_password_validator import ( 5 | ZxcvbnPasswordValidator, 6 | ) 7 | 8 | __all__ = ["DEFAULT_MINIMAL_STRENGTH", "ZxcvbnPasswordValidator"] 9 | -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/locale/ar/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pierre-Sassoulas/django-zxcvbn-password-validator/960b6ac286a97742465fd4046c16a782c4463830/django_zxcvbn_password_validator/locale/ar/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/locale/ar/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # django_zxcvbn_password_validator's french traduction 2 | # This file is distributed under the MIT license. 3 | # Pierre SASSOULAS , 2018. 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: PACKAGE VERSION\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2025-04-08 05:53+0000\n" 10 | "PO-Revision-Date: 2025-02-26 23:47+0000\n" 11 | "Last-Translator: ButterflyOfFire \n" 12 | "Language-Team: Arabic \n" 14 | "Language: ar\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " 19 | "&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" 20 | "X-Generator: Weblate 5.10.2-dev\n" 21 | "X-Translated-Using: django-rosetta 0.9.8\n" 22 | 23 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:24 24 | msgid "Use a few words, avoid common phrases" 25 | msgstr "استخدم بضع كلمات، وتجنب العبارات الشائعة" 26 | 27 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:27 28 | msgid "No need for symbols, digits, or uppercase letters" 29 | msgstr "لا حاجة للرموز أو الأرقام أو الحروف الكبيرة" 30 | 31 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:30 32 | msgid "Add another word or two. Uncommon words are better." 33 | msgstr "أضف كلمة أو كلمتين أخريين. الكلمات الأقل شيوعاً هي الأفضل." 34 | 35 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:33 36 | msgid "Straight rows of keys are easy to guess" 37 | msgstr "محاذاة المفاتيح سهلة التخمين" 38 | 39 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:36 40 | msgid "Short keyboard patterns are easy to guess" 41 | msgstr "من السهل تخمين الأنماط على لوحة المفاتيح إذا كانت قصيرة" 42 | 43 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:39 44 | msgid "Use a longer keyboard pattern with more turns" 45 | msgstr "" 46 | 47 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:42 48 | msgid "Repeats like \"aaa\" are easy to guess" 49 | msgstr "" 50 | 51 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:45 52 | msgid "" 53 | "Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\"" 54 | msgstr "" 55 | 56 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:47 57 | msgid "Avoid repeated words and characters" 58 | msgstr "" 59 | 60 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:49 61 | msgid "Sequences like \"abc\" or \"6543\" are easy to guess" 62 | msgstr "" 63 | 64 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:51 65 | msgid "Avoid sequences" 66 | msgstr "" 67 | 68 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:52 69 | msgid "Recent years are easy to guess" 70 | msgstr "" 71 | 72 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:53 73 | msgid "Avoid recent years" 74 | msgstr "تجنب السنوات الأخيرة" 75 | 76 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:55 77 | msgid "Avoid years that are associated with you" 78 | msgstr "" 79 | 80 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:57 81 | msgid "Dates are often easy to guess" 82 | msgstr "" 83 | 84 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:59 85 | msgid "Avoid dates and years that are associated with you" 86 | msgstr "" 87 | 88 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:61 89 | msgid "This is a top-10 common password" 90 | msgstr "" 91 | 92 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:62 93 | msgid "This is a top-100 common password" 94 | msgstr "" 95 | 96 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:63 97 | msgid "This is a very common password" 98 | msgstr "هذه كلمة مرور شائعة جدا" 99 | 100 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:65 101 | msgid "This is similar to a commonly used password" 102 | msgstr "هذه تشبه كلمة مرور شائعة الاستخدام" 103 | 104 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:67 105 | msgid "A word by itself is easy to guess" 106 | msgstr "" 107 | 108 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:69 109 | msgid "Names and surnames by themselves are easy to guess" 110 | msgstr "" 111 | 112 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:72 113 | msgid "Common names and surnames are easy to guess" 114 | msgstr "من السهل جدا تخمين الأسماء والألقاب الشائعة" 115 | 116 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:75 117 | msgid "Capitalization doesn't help very much" 118 | msgstr "" 119 | 120 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:78 121 | msgid "All-uppercase is almost as easy to guess as all-lowercase" 122 | msgstr "" 123 | 124 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:81 125 | msgid "Reversed words aren't much harder to guess" 126 | msgstr "" 127 | 128 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:84 129 | msgid "Predictable substitutions like '@' instead of 'a' don't help very much" 130 | msgstr "" 131 | 132 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:110 133 | msgid "less than a second" 134 | msgstr "أقل من ثانية" 135 | 136 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:113 137 | #, fuzzy, python-format 138 | #| msgid "second" 139 | msgid "%d second" 140 | msgid_plural "%d seconds" 141 | msgstr[0] "ثانية" 142 | msgstr[1] "ثانية" 143 | msgstr[2] "ثانية" 144 | msgstr[3] "ثانية" 145 | msgstr[4] "ثانية" 146 | msgstr[5] "ثانية" 147 | 148 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:116 149 | #, fuzzy, python-format 150 | #| msgid "minute" 151 | msgid "%d minute" 152 | msgid_plural "%d minutes" 153 | msgstr[0] "دقيقة" 154 | msgstr[1] "دقيقة" 155 | msgstr[2] "دقيقة" 156 | msgstr[3] "دقيقة" 157 | msgstr[4] "دقيقة" 158 | msgstr[5] "دقيقة" 159 | 160 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:119 161 | #, fuzzy, python-format 162 | #| msgid "hour" 163 | msgid "%d hour" 164 | msgid_plural "%d hours" 165 | msgstr[0] "ساعة" 166 | msgstr[1] "ساعة" 167 | msgstr[2] "ساعة" 168 | msgstr[3] "ساعة" 169 | msgstr[4] "ساعة" 170 | msgstr[5] "ساعة" 171 | 172 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:122 173 | #, python-format 174 | msgid "%d day" 175 | msgid_plural "%d days" 176 | msgstr[0] "" 177 | msgstr[1] "" 178 | msgstr[2] "" 179 | msgstr[3] "" 180 | msgstr[4] "" 181 | msgstr[5] "" 182 | 183 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:125 184 | #, fuzzy, python-format 185 | #| msgid "month" 186 | msgid "%d month" 187 | msgid_plural "%d months" 188 | msgstr[0] "شهر" 189 | msgstr[1] "شهر" 190 | msgstr[2] "شهر" 191 | msgstr[3] "شهر" 192 | msgstr[4] "شهر" 193 | msgstr[5] "شهر" 194 | 195 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:128 196 | #, fuzzy, python-format 197 | #| msgid "year" 198 | msgid "%d year" 199 | msgid_plural "%d year" 200 | msgstr[0] "سنة" 201 | msgstr[1] "سنة" 202 | msgstr[2] "سنة" 203 | msgstr[3] "سنة" 204 | msgstr[4] "سنة" 205 | msgstr[5] "سنة" 206 | 207 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:129 208 | msgid "centuries" 209 | msgstr "قرون" 210 | 211 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:71 212 | msgid "Your password exceeds the maximal length of 72 characters." 213 | msgstr "" 214 | 215 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:80 216 | #, fuzzy 217 | #| msgid "Your password is too guessable :" 218 | msgid "Your password is too guessable:" 219 | msgstr "من السهل جداً تخمين كلمة مرورك:" 220 | 221 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:81 222 | #, python-format 223 | msgid "It would take an offline attacker %(time)s to guess it." 224 | msgstr "سيتطلب من مهاجم غير متصل بالإنترنت %(time)s لتخمينها." 225 | 226 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:86 227 | #, fuzzy 228 | #| msgid "Warning" 229 | msgid "Warning:" 230 | msgstr "تحذير" 231 | 232 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:89 233 | #, fuzzy 234 | #| msgid "Advice" 235 | msgid "Advice:" 236 | msgstr "نصيحة" 237 | 238 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:95 239 | msgid "We expect nothing: you can use any password you want." 240 | msgstr "" 241 | 242 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:96 243 | #, python-format 244 | msgid "We expect a password that cannot be guessed %s" 245 | msgstr "" 246 | 247 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:98 248 | msgid "by your familly or friends." 249 | msgstr "" 250 | 251 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:99 252 | msgid "by attackers online." 253 | msgstr "من قبل مهاجِمين عبر الإنترنت." 254 | 255 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:100 256 | msgid "without access to our database." 257 | msgstr "" 258 | 259 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:101 260 | msgid "without a dedicated team and an access to our database." 261 | msgstr "" 262 | 263 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:105 264 | msgid "There is no specific rule for a great password," 265 | msgstr "ليس هناك قاعدة محددة لكلمة مرور قوية،" 266 | 267 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:106 268 | msgid "however if your password is too easy to guess," 269 | msgstr "ومع ذلك إذا كانت كلمة مرورك سهلة التخمين،" 270 | 271 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:107 272 | msgid "we will tell you how to make a better one." 273 | msgstr "" 274 | 275 | #~ msgid "seconds" 276 | #~ msgstr "ثوان" 277 | 278 | #~ msgid "minutes" 279 | #~ msgstr "دقائق" 280 | 281 | #~ msgid "hours" 282 | #~ msgstr "ساعات" 283 | 284 | #~ msgid "days" 285 | #~ msgstr "أيام" 286 | 287 | #~ msgid "months" 288 | #~ msgstr "أشهر" 289 | 290 | #~ msgid "years" 291 | #~ msgstr "سنوات" 292 | 293 | #~ msgid "day" 294 | #~ msgstr "يوم" 295 | -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/locale/cs/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pierre-Sassoulas/django-zxcvbn-password-validator/960b6ac286a97742465fd4046c16a782c4463830/django_zxcvbn_password_validator/locale/cs/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/locale/cs/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Czehc translation for django_zxcvbn_password_validator 2 | # This file is distributed under the MIT license. 3 | # Michal Čihař , 2025. 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: PACKAGE VERSION\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2025-04-08 05:53+0000\n" 10 | "PO-Revision-Date: 2025-04-09 10:03+0000\n" 11 | "Last-Translator: Michal Čihař \n" 12 | "Language-Team: Czech \n" 14 | "Language: cs\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n " 19 | "<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" 20 | "X-Generator: Weblate 5.11-dev\n" 21 | 22 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:24 23 | msgid "Use a few words, avoid common phrases" 24 | msgstr "Použijte několik slov, vyhněte se běžným frázím" 25 | 26 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:27 27 | msgid "No need for symbols, digits, or uppercase letters" 28 | msgstr "Není třeba používat symboly, číslice nebo velká písmena" 29 | 30 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:30 31 | msgid "Add another word or two. Uncommon words are better." 32 | msgstr "Přidejte jedno nebo dvě slova. Méně obvyklá slova jsou lepší." 33 | 34 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:33 35 | msgid "Straight rows of keys are easy to guess" 36 | msgstr "(na klávesnici) po sobě jdoucí písmena jsou snadno uhodnutelná" 37 | 38 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:36 39 | msgid "Short keyboard patterns are easy to guess" 40 | msgstr "Krátké vzory na klávesnici jsou snadno uhodnutelné" 41 | 42 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:39 43 | msgid "Use a longer keyboard pattern with more turns" 44 | msgstr "Použijte delší klávesnicové vzory s více otočkami" 45 | 46 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:42 47 | msgid "Repeats like \"aaa\" are easy to guess" 48 | msgstr "Opakování jako např. „aaa“ jsou snadno uhodnutelné" 49 | 50 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:45 51 | msgid "" 52 | "Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\"" 53 | msgstr "" 54 | "Opakování jako např. „abcabcabc“ jsou jen o málo méně uhodnutelné než „abc“" 55 | 56 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:47 57 | msgid "Avoid repeated words and characters" 58 | msgstr "Vyhněte se opakování slov a znaků" 59 | 60 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:49 61 | msgid "Sequences like \"abc\" or \"6543\" are easy to guess" 62 | msgstr "Posloupnosti, jako třeba „abc“ nebo „6543“, jsou snadno uhodnutelné" 63 | 64 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:51 65 | msgid "Avoid sequences" 66 | msgstr "Vyhněte se posloupnostem" 67 | 68 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:52 69 | msgid "Recent years are easy to guess" 70 | msgstr "Nedávné roky jsou snadno uhodnutelné" 71 | 72 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:53 73 | msgid "Avoid recent years" 74 | msgstr "Vyhněte se nedávným rokům" 75 | 76 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:55 77 | msgid "Avoid years that are associated with you" 78 | msgstr "Vyhněte se rokům, které jsou s vámi spojeny" 79 | 80 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:57 81 | msgid "Dates are often easy to guess" 82 | msgstr "Data jsou často snadno uhodnutelná" 83 | 84 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:59 85 | msgid "Avoid dates and years that are associated with you" 86 | msgstr "Vyhněte se datům a rokům, která s vámi mají nějakou souvislost" 87 | 88 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:61 89 | msgid "This is a top-10 common password" 90 | msgstr "Toto heslo patří mezi 10 nejpoužívanějších" 91 | 92 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:62 93 | msgid "This is a top-100 common password" 94 | msgstr "Toto heslo patří mezi 100 nejpoužívanějších" 95 | 96 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:63 97 | msgid "This is a very common password" 98 | msgstr "Toto heslo patří mezi nejpoužívanější" 99 | 100 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:65 101 | msgid "This is similar to a commonly used password" 102 | msgstr "Toto heslo je podobné nejpoužívanějšímu" 103 | 104 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:67 105 | msgid "A word by itself is easy to guess" 106 | msgstr "Samotné slovo je snadné uhodnout" 107 | 108 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:69 109 | msgid "Names and surnames by themselves are easy to guess" 110 | msgstr "Samotná jména a příjmení je snadné uhodnout" 111 | 112 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:72 113 | msgid "Common names and surnames are easy to guess" 114 | msgstr "Běžná jména a příjmení je snadné uhodnout" 115 | 116 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:75 117 | msgid "Capitalization doesn't help very much" 118 | msgstr "Psaní velkých písmen moc nepomůže" 119 | 120 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:78 121 | msgid "All-uppercase is almost as easy to guess as all-lowercase" 122 | msgstr "" 123 | "Všechna velká písmena jsou téměř stejně snadno uhodnutelná jako všechna malá" 124 | 125 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:81 126 | msgid "Reversed words aren't much harder to guess" 127 | msgstr "Slova pozpátku nejsou moc těžká na uhodnutí" 128 | 129 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:84 130 | msgid "Predictable substitutions like '@' instead of 'a' don't help very much" 131 | msgstr "Předvídatelná nahrazení jako „@“ místo „a“ moc nepomohou" 132 | 133 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:110 134 | msgid "less than a second" 135 | msgstr "méně než sekundu" 136 | 137 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:113 138 | #, python-format 139 | msgid "%d second" 140 | msgid_plural "%d seconds" 141 | msgstr[0] "%d sekundu" 142 | msgstr[1] "%d sekundy" 143 | msgstr[2] "%d sekund" 144 | msgstr[3] "%d sekund" 145 | 146 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:116 147 | #, python-format 148 | msgid "%d minute" 149 | msgid_plural "%d minutes" 150 | msgstr[0] "%d minutu" 151 | msgstr[1] "%d minuty" 152 | msgstr[2] "%d minut" 153 | msgstr[3] "%d minut" 154 | 155 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:119 156 | #, python-format 157 | msgid "%d hour" 158 | msgid_plural "%d hours" 159 | msgstr[0] "%d hodinu" 160 | msgstr[1] "%d hodiny" 161 | msgstr[2] "%d hodin" 162 | msgstr[3] "%d hodin" 163 | 164 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:122 165 | #, python-format 166 | msgid "%d day" 167 | msgid_plural "%d days" 168 | msgstr[0] "%d den" 169 | msgstr[1] "%d dny" 170 | msgstr[2] "%d dnů" 171 | msgstr[3] "%d dnů" 172 | 173 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:125 174 | #, python-format 175 | msgid "%d month" 176 | msgid_plural "%d months" 177 | msgstr[0] "%d měsíc" 178 | msgstr[1] "%d měsíce" 179 | msgstr[2] "%d měsíců" 180 | msgstr[3] "%d měsíců" 181 | 182 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:128 183 | #, python-format 184 | msgid "%d year" 185 | msgid_plural "%d year" 186 | msgstr[0] "%d rok" 187 | msgstr[1] "%d roky" 188 | msgstr[2] "%d let" 189 | msgstr[3] "%d let" 190 | 191 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:129 192 | msgid "centuries" 193 | msgstr "století" 194 | 195 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:71 196 | msgid "Your password exceeds the maximal length of 72 characters." 197 | msgstr "Vaše heslo překračuje maximální délku 72 znaků." 198 | 199 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:80 200 | msgid "Your password is too guessable:" 201 | msgstr "Vaše heslo je snadno uhodnutelné:" 202 | 203 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:81 204 | #, python-format 205 | msgid "It would take an offline attacker %(time)s to guess it." 206 | msgstr "Offline útočníkovi by trvalo %(time)s, než by ho uhodl." 207 | 208 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:86 209 | msgid "Warning:" 210 | msgstr "Varování:" 211 | 212 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:89 213 | msgid "Advice:" 214 | msgstr "Doporučení:" 215 | 216 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:95 217 | msgid "We expect nothing: you can use any password you want." 218 | msgstr "Neočekáváme nic: můžete použít jakékoliv heslo." 219 | 220 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:96 221 | #, python-format 222 | msgid "We expect a password that cannot be guessed %s" 223 | msgstr "Očekáváme, že heslo nepůjde uhodnout %s" 224 | 225 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:98 226 | msgid "by your familly or friends." 227 | msgstr "vaší rodinou nebo přáteli." 228 | 229 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:99 230 | msgid "by attackers online." 231 | msgstr "útočníkem online." 232 | 233 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:100 234 | msgid "without access to our database." 235 | msgstr "bez přístupu k naší databázi." 236 | 237 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:101 238 | msgid "without a dedicated team and an access to our database." 239 | msgstr "bez vyhrazeného týmu a přístupu k naší databázi." 240 | 241 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:105 242 | msgid "There is no specific rule for a great password," 243 | msgstr "Neexistují žádná specifická pravidla pro dokonalé heslo," 244 | 245 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:106 246 | msgid "however if your password is too easy to guess," 247 | msgstr "ale pokud je vaše heslo příliš snadné odhadnout," 248 | 249 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:107 250 | msgid "we will tell you how to make a better one." 251 | msgstr "řekneme vám, jak vytvořit lepší." 252 | -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/locale/fr/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pierre-Sassoulas/django-zxcvbn-password-validator/960b6ac286a97742465fd4046c16a782c4463830/django_zxcvbn_password_validator/locale/fr/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/locale/fr/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # django_zxcvbn_password_validator's french traduction 2 | # This file is distributed under the MIT license. 3 | # Pierre SASSOULAS , 2018. 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: PACKAGE VERSION\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2025-04-08 05:53+0000\n" 10 | "PO-Revision-Date: 2025-04-09 10:03+0000\n" 11 | "Last-Translator: Pierre Sassoulas \n" 12 | "Language-Team: French \n" 14 | "Language: fr\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=n > 1;\n" 19 | "X-Generator: Weblate 5.11-dev\n" 20 | "X-Translated-Using: django-rosetta 0.9.8\n" 21 | 22 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:24 23 | msgid "Use a few words, avoid common phrases" 24 | msgstr "Utilisez quelques mots, évitez les mots souvent utilisés ensemble" 25 | 26 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:27 27 | msgid "No need for symbols, digits, or uppercase letters" 28 | msgstr "Pas besoin de symboles, nombres, ou lettres majuscules" 29 | 30 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:30 31 | msgid "Add another word or two. Uncommon words are better." 32 | msgstr "" 33 | "Ajoutez un ou deux autres mots. Les mots les moins courants sont les " 34 | "meilleurs." 35 | 36 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:33 37 | msgid "Straight rows of keys are easy to guess" 38 | msgstr "Les alignements de touches sont faciles à deviner" 39 | 40 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:36 41 | msgid "Short keyboard patterns are easy to guess" 42 | msgstr "Les motifs sur le clavier sont faciles à deviner s'ils sont courts" 43 | 44 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:39 45 | msgid "Use a longer keyboard pattern with more turns" 46 | msgstr "" 47 | "Utilisez un motif plus long sur le clavier, avec plus de changement de " 48 | "direction" 49 | 50 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:42 51 | msgid "Repeats like \"aaa\" are easy to guess" 52 | msgstr "Les répétitions du type \"aaa\" sont faciles à deviner" 53 | 54 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:45 55 | msgid "" 56 | "Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\"" 57 | msgstr "" 58 | "Les répétitions du type \"abcabcabc\" sont à peine plus dures à deviner que " 59 | "\"abc\"" 60 | 61 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:47 62 | msgid "Avoid repeated words and characters" 63 | msgstr "Évitez de répéter des mots ou des caractères" 64 | 65 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:49 66 | msgid "Sequences like \"abc\" or \"6543\" are easy to guess" 67 | msgstr "Les séquences du type \"abc\" ou \"6543\" sont faciles à deviner" 68 | 69 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:51 70 | msgid "Avoid sequences" 71 | msgstr "Évitez les séquences" 72 | 73 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:52 74 | msgid "Recent years are easy to guess" 75 | msgstr "Les années récentes sont faciles à deviner" 76 | 77 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:53 78 | msgid "Avoid recent years" 79 | msgstr "Évitez les années récentes" 80 | 81 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:55 82 | msgid "Avoid years that are associated with you" 83 | msgstr "Évitez les années associés aux événements de votre vie" 84 | 85 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:57 86 | msgid "Dates are often easy to guess" 87 | msgstr "Les dates sont souvent faciles à deviner" 88 | 89 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:59 90 | msgid "Avoid dates and years that are associated with you" 91 | msgstr "Évitez les dates et les années associées aux événements de votre vie" 92 | 93 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:61 94 | msgid "This is a top-10 common password" 95 | msgstr "C'est l'un des 10 mot de passe les plus courants" 96 | 97 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:62 98 | msgid "This is a top-100 common password" 99 | msgstr "C'est l'un des 100 mot de passe les plus courants" 100 | 101 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:63 102 | msgid "This is a very common password" 103 | msgstr "C'est un mot de passe très courant" 104 | 105 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:65 106 | msgid "This is similar to a commonly used password" 107 | msgstr "C'est proche d'un mot de passe très courant" 108 | 109 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:67 110 | msgid "A word by itself is easy to guess" 111 | msgstr "Un mot isolé est facile à deviner" 112 | 113 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:69 114 | msgid "Names and surnames by themselves are easy to guess" 115 | msgstr "Les noms et prénoms, pris de manière isolés, sont faciles à deviner" 116 | 117 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:72 118 | msgid "Common names and surnames are easy to guess" 119 | msgstr "Les noms et prénoms courants sont faciles à deviner" 120 | 121 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:75 122 | msgid "Capitalization doesn't help very much" 123 | msgstr "Utiliser des majuscules n'aide pas beaucoup" 124 | 125 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:78 126 | msgid "All-uppercase is almost as easy to guess as all-lowercase" 127 | msgstr "Tout-en-majuscule est à peine plus dur à deviner que tout-en-minuscule" 128 | 129 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:81 130 | msgid "Reversed words aren't much harder to guess" 131 | msgstr "Un mot écrit à l'envers n'est pas beaucoup plus dur à deviner" 132 | 133 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:84 134 | msgid "Predictable substitutions like '@' instead of 'a' don't help very much" 135 | msgstr "" 136 | "Les substitutions prévisibles comme «@»à la place de «a» n'aident pas " 137 | "beaucoup" 138 | 139 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:110 140 | msgid "less than a second" 141 | msgstr "moins d'une seconde" 142 | 143 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:113 144 | #, python-format 145 | msgid "%d second" 146 | msgid_plural "%d seconds" 147 | msgstr[0] "%d seconde" 148 | msgstr[1] "%d secondes" 149 | 150 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:116 151 | #, python-format 152 | msgid "%d minute" 153 | msgid_plural "%d minutes" 154 | msgstr[0] "%d minute" 155 | msgstr[1] "%d minutes" 156 | 157 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:119 158 | #, python-format 159 | msgid "%d hour" 160 | msgid_plural "%d hours" 161 | msgstr[0] "%d heure" 162 | msgstr[1] "%d heures" 163 | 164 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:122 165 | #, python-format 166 | msgid "%d day" 167 | msgid_plural "%d days" 168 | msgstr[0] "%d jour" 169 | msgstr[1] "%d jours" 170 | 171 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:125 172 | #, python-format 173 | msgid "%d month" 174 | msgid_plural "%d months" 175 | msgstr[0] "%d mois" 176 | msgstr[1] "%d mois" 177 | 178 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:128 179 | #, python-format 180 | msgid "%d year" 181 | msgid_plural "%d year" 182 | msgstr[0] "%d année" 183 | msgstr[1] "%d années" 184 | 185 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:129 186 | msgid "centuries" 187 | msgstr "siècles" 188 | 189 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:71 190 | msgid "Your password exceeds the maximal length of 72 characters." 191 | msgstr "Votre mot de passe dépasse la taille maximale de 72 caractères." 192 | 193 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:80 194 | msgid "Your password is too guessable:" 195 | msgstr "Votre mot de passe est trop facile à deviner :" 196 | 197 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:81 198 | #, python-format 199 | msgid "It would take an offline attacker %(time)s to guess it." 200 | msgstr "" 201 | "Le deviner prendrait %(time)s à un attaquant qui aurait copié notre base de " 202 | "données." 203 | 204 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:86 205 | msgid "Warning:" 206 | msgstr "Attention :" 207 | 208 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:89 209 | msgid "Advice:" 210 | msgstr "Conseil :" 211 | 212 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:95 213 | msgid "We expect nothing: you can use any password you want." 214 | msgstr "" 215 | "Nous n’exigeons rien : vous pouvez utiliser n'importe quel mot de passe." 216 | 217 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:96 218 | #, python-format 219 | msgid "We expect a password that cannot be guessed %s" 220 | msgstr "Nous demandons un mot de passe qui ne peut pas être deviné %s" 221 | 222 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:98 223 | msgid "by your familly or friends." 224 | msgstr "par votre famille ou vos amis." 225 | 226 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:99 227 | msgid "by attackers online." 228 | msgstr "par des attaquants sur internet." 229 | 230 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:100 231 | msgid "without access to our database." 232 | msgstr "à moins d'avoir accès à notre base de données." 233 | 234 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:101 235 | msgid "without a dedicated team and an access to our database." 236 | msgstr "à moins d'avoir une équipe dédiée et un accès à notre base de données." 237 | 238 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:105 239 | msgid "There is no specific rule for a great password," 240 | msgstr "Il n'y a pas de règle absolue pour un bon mot de passe," 241 | 242 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:106 243 | msgid "however if your password is too easy to guess," 244 | msgstr "cependant si votre mot de passe est trop facile à deviner," 245 | 246 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:107 247 | msgid "we will tell you how to make a better one." 248 | msgstr "nous vous dirons comment l'améliorer." 249 | 250 | #~ msgid "We expect" 251 | #~ msgstr "Nous" 252 | 253 | #~ msgid "seconds" 254 | #~ msgstr "secondes" 255 | 256 | #~ msgid "minutes" 257 | #~ msgstr "minutes" 258 | 259 | #~ msgid "hours" 260 | #~ msgstr "heures" 261 | 262 | #~ msgid "days" 263 | #~ msgstr "jours" 264 | 265 | #~ msgid "months" 266 | #~ msgstr "mois" 267 | 268 | #~ msgid "years" 269 | #~ msgstr "années" 270 | 271 | #~ msgid "day" 272 | #~ msgstr "jour" 273 | -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/locale/hu/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pierre-Sassoulas/django-zxcvbn-password-validator/960b6ac286a97742465fd4046c16a782c4463830/django_zxcvbn_password_validator/locale/hu/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/locale/hu/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # django_zxcvbn_password_validator's hungarian traduction 2 | # This file is distributed under the MIT license. 3 | # Pierre SASSOULAS , 2018. 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: PACKAGE VERSION\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2025-04-08 05:53+0000\n" 10 | "PO-Revision-Date: 2025-04-09 10:03+0000\n" 11 | "Last-Translator: Automatically generated\n" 12 | "Language-Team: none\n" 13 | "Language: hu\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 18 | "X-Generator: Weblate 5.11-dev\n" 19 | "X-Translated-Using: django-rosetta 0.9.8\n" 20 | 21 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:24 22 | msgid "Use a few words, avoid common phrases" 23 | msgstr "" 24 | 25 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:27 26 | msgid "No need for symbols, digits, or uppercase letters" 27 | msgstr "" 28 | 29 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:30 30 | msgid "Add another word or two. Uncommon words are better." 31 | msgstr "" 32 | 33 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:33 34 | msgid "Straight rows of keys are easy to guess" 35 | msgstr "" 36 | 37 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:36 38 | msgid "Short keyboard patterns are easy to guess" 39 | msgstr "" 40 | 41 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:39 42 | msgid "Use a longer keyboard pattern with more turns" 43 | msgstr "" 44 | 45 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:42 46 | msgid "Repeats like \"aaa\" are easy to guess" 47 | msgstr "" 48 | 49 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:45 50 | msgid "" 51 | "Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\"" 52 | msgstr "" 53 | 54 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:47 55 | msgid "Avoid repeated words and characters" 56 | msgstr "" 57 | 58 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:49 59 | msgid "Sequences like \"abc\" or \"6543\" are easy to guess" 60 | msgstr "" 61 | 62 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:51 63 | msgid "Avoid sequences" 64 | msgstr "" 65 | 66 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:52 67 | msgid "Recent years are easy to guess" 68 | msgstr "" 69 | 70 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:53 71 | msgid "Avoid recent years" 72 | msgstr "" 73 | 74 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:55 75 | msgid "Avoid years that are associated with you" 76 | msgstr "" 77 | 78 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:57 79 | msgid "Dates are often easy to guess" 80 | msgstr "" 81 | 82 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:59 83 | msgid "Avoid dates and years that are associated with you" 84 | msgstr "" 85 | 86 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:61 87 | msgid "This is a top-10 common password" 88 | msgstr "" 89 | 90 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:62 91 | msgid "This is a top-100 common password" 92 | msgstr "" 93 | 94 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:63 95 | msgid "This is a very common password" 96 | msgstr "" 97 | 98 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:65 99 | msgid "This is similar to a commonly used password" 100 | msgstr "" 101 | 102 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:67 103 | msgid "A word by itself is easy to guess" 104 | msgstr "" 105 | 106 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:69 107 | msgid "Names and surnames by themselves are easy to guess" 108 | msgstr "" 109 | 110 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:72 111 | msgid "Common names and surnames are easy to guess" 112 | msgstr "" 113 | 114 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:75 115 | msgid "Capitalization doesn't help very much" 116 | msgstr "" 117 | 118 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:78 119 | msgid "All-uppercase is almost as easy to guess as all-lowercase" 120 | msgstr "" 121 | 122 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:81 123 | msgid "Reversed words aren't much harder to guess" 124 | msgstr "" 125 | 126 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:84 127 | msgid "Predictable substitutions like '@' instead of 'a' don't help very much" 128 | msgstr "" 129 | 130 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:110 131 | msgid "less than a second" 132 | msgstr "" 133 | 134 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:113 135 | #, python-format 136 | msgid "%d second" 137 | msgid_plural "%d seconds" 138 | msgstr[0] "" 139 | msgstr[1] "" 140 | 141 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:116 142 | #, python-format 143 | msgid "%d minute" 144 | msgid_plural "%d minutes" 145 | msgstr[0] "" 146 | msgstr[1] "" 147 | 148 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:119 149 | #, python-format 150 | msgid "%d hour" 151 | msgid_plural "%d hours" 152 | msgstr[0] "" 153 | msgstr[1] "" 154 | 155 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:122 156 | #, python-format 157 | msgid "%d day" 158 | msgid_plural "%d days" 159 | msgstr[0] "" 160 | msgstr[1] "" 161 | 162 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:125 163 | #, python-format 164 | msgid "%d month" 165 | msgid_plural "%d months" 166 | msgstr[0] "" 167 | msgstr[1] "" 168 | 169 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:128 170 | #, python-format 171 | msgid "%d year" 172 | msgid_plural "%d year" 173 | msgstr[0] "" 174 | msgstr[1] "" 175 | 176 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:129 177 | msgid "centuries" 178 | msgstr "" 179 | 180 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:71 181 | msgid "Your password exceeds the maximal length of 72 characters." 182 | msgstr "" 183 | 184 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:80 185 | msgid "Your password is too guessable:" 186 | msgstr "" 187 | 188 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:81 189 | #, python-format 190 | msgid "It would take an offline attacker %(time)s to guess it." 191 | msgstr "" 192 | 193 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:86 194 | msgid "Warning:" 195 | msgstr "" 196 | 197 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:89 198 | msgid "Advice:" 199 | msgstr "" 200 | 201 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:95 202 | msgid "We expect nothing: you can use any password you want." 203 | msgstr "" 204 | 205 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:96 206 | #, python-format 207 | msgid "We expect a password that cannot be guessed %s" 208 | msgstr "" 209 | 210 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:98 211 | msgid "by your familly or friends." 212 | msgstr "" 213 | 214 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:99 215 | msgid "by attackers online." 216 | msgstr "" 217 | 218 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:100 219 | msgid "without access to our database." 220 | msgstr "" 221 | 222 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:101 223 | msgid "without a dedicated team and an access to our database." 224 | msgstr "" 225 | 226 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:105 227 | msgid "There is no specific rule for a great password," 228 | msgstr "" 229 | 230 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:106 231 | msgid "however if your password is too easy to guess," 232 | msgstr "" 233 | 234 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:107 235 | msgid "we will tell you how to make a better one." 236 | msgstr "" 237 | 238 | #~ msgid "We expect" 239 | #~ msgstr "Nous" 240 | 241 | #~ msgid "seconds" 242 | #~ msgstr "secondes" 243 | 244 | #~ msgid "minutes" 245 | #~ msgstr "minutes" 246 | 247 | #~ msgid "hours" 248 | #~ msgstr "heures" 249 | 250 | #~ msgid "days" 251 | #~ msgstr "jours" 252 | 253 | #~ msgid "months" 254 | #~ msgstr "mois" 255 | 256 | #~ msgid "years" 257 | #~ msgstr "années" 258 | 259 | #~ msgid "day" 260 | #~ msgstr "jour" 261 | -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/locale/id/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pierre-Sassoulas/django-zxcvbn-password-validator/960b6ac286a97742465fd4046c16a782c4463830/django_zxcvbn_password_validator/locale/id/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/locale/id/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # django_zxcvbn_password_validator's french traduction 2 | # This file is distributed under the MIT license. 3 | # Pierre SASSOULAS , 2018. 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: PACKAGE VERSION\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2025-04-08 05:53+0000\n" 10 | "PO-Revision-Date: 2025-02-26 10:08+0000\n" 11 | "Last-Translator: Automatically generated\n" 12 | "Language-Team: none\n" 13 | "Language: id\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=1; plural=0;\n" 18 | "X-Generator: Weblate 5.10.2-dev\n" 19 | "X-Translated-Using: django-rosetta 0.9.8\n" 20 | 21 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:24 22 | msgid "Use a few words, avoid common phrases" 23 | msgstr "" 24 | 25 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:27 26 | msgid "No need for symbols, digits, or uppercase letters" 27 | msgstr "" 28 | 29 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:30 30 | msgid "Add another word or two. Uncommon words are better." 31 | msgstr "" 32 | 33 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:33 34 | msgid "Straight rows of keys are easy to guess" 35 | msgstr "" 36 | 37 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:36 38 | msgid "Short keyboard patterns are easy to guess" 39 | msgstr "" 40 | 41 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:39 42 | msgid "Use a longer keyboard pattern with more turns" 43 | msgstr "" 44 | 45 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:42 46 | msgid "Repeats like \"aaa\" are easy to guess" 47 | msgstr "" 48 | 49 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:45 50 | msgid "" 51 | "Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\"" 52 | msgstr "" 53 | 54 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:47 55 | msgid "Avoid repeated words and characters" 56 | msgstr "" 57 | 58 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:49 59 | msgid "Sequences like \"abc\" or \"6543\" are easy to guess" 60 | msgstr "" 61 | 62 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:51 63 | msgid "Avoid sequences" 64 | msgstr "" 65 | 66 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:52 67 | msgid "Recent years are easy to guess" 68 | msgstr "" 69 | 70 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:53 71 | msgid "Avoid recent years" 72 | msgstr "" 73 | 74 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:55 75 | msgid "Avoid years that are associated with you" 76 | msgstr "" 77 | 78 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:57 79 | msgid "Dates are often easy to guess" 80 | msgstr "" 81 | 82 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:59 83 | msgid "Avoid dates and years that are associated with you" 84 | msgstr "" 85 | 86 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:61 87 | msgid "This is a top-10 common password" 88 | msgstr "" 89 | 90 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:62 91 | msgid "This is a top-100 common password" 92 | msgstr "" 93 | 94 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:63 95 | msgid "This is a very common password" 96 | msgstr "" 97 | 98 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:65 99 | msgid "This is similar to a commonly used password" 100 | msgstr "" 101 | 102 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:67 103 | msgid "A word by itself is easy to guess" 104 | msgstr "" 105 | 106 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:69 107 | msgid "Names and surnames by themselves are easy to guess" 108 | msgstr "" 109 | 110 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:72 111 | msgid "Common names and surnames are easy to guess" 112 | msgstr "" 113 | 114 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:75 115 | msgid "Capitalization doesn't help very much" 116 | msgstr "" 117 | 118 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:78 119 | msgid "All-uppercase is almost as easy to guess as all-lowercase" 120 | msgstr "" 121 | 122 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:81 123 | msgid "Reversed words aren't much harder to guess" 124 | msgstr "" 125 | 126 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:84 127 | msgid "Predictable substitutions like '@' instead of 'a' don't help very much" 128 | msgstr "" 129 | 130 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:110 131 | msgid "less than a second" 132 | msgstr "" 133 | 134 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:113 135 | #, python-format 136 | msgid "%d second" 137 | msgid_plural "%d seconds" 138 | msgstr[0] "" 139 | 140 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:116 141 | #, python-format 142 | msgid "%d minute" 143 | msgid_plural "%d minutes" 144 | msgstr[0] "" 145 | 146 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:119 147 | #, python-format 148 | msgid "%d hour" 149 | msgid_plural "%d hours" 150 | msgstr[0] "" 151 | 152 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:122 153 | #, python-format 154 | msgid "%d day" 155 | msgid_plural "%d days" 156 | msgstr[0] "" 157 | 158 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:125 159 | #, python-format 160 | msgid "%d month" 161 | msgid_plural "%d months" 162 | msgstr[0] "" 163 | 164 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:128 165 | #, python-format 166 | msgid "%d year" 167 | msgid_plural "%d year" 168 | msgstr[0] "" 169 | 170 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:129 171 | msgid "centuries" 172 | msgstr "" 173 | 174 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:71 175 | msgid "Your password exceeds the maximal length of 72 characters." 176 | msgstr "" 177 | 178 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:80 179 | msgid "Your password is too guessable:" 180 | msgstr "" 181 | 182 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:81 183 | #, python-format 184 | msgid "It would take an offline attacker %(time)s to guess it." 185 | msgstr "" 186 | 187 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:86 188 | msgid "Warning:" 189 | msgstr "" 190 | 191 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:89 192 | msgid "Advice:" 193 | msgstr "" 194 | 195 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:95 196 | msgid "We expect nothing: you can use any password you want." 197 | msgstr "" 198 | 199 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:96 200 | #, python-format 201 | msgid "We expect a password that cannot be guessed %s" 202 | msgstr "" 203 | 204 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:98 205 | msgid "by your familly or friends." 206 | msgstr "" 207 | 208 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:99 209 | msgid "by attackers online." 210 | msgstr "" 211 | 212 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:100 213 | msgid "without access to our database." 214 | msgstr "" 215 | 216 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:101 217 | msgid "without a dedicated team and an access to our database." 218 | msgstr "" 219 | 220 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:105 221 | msgid "There is no specific rule for a great password," 222 | msgstr "" 223 | 224 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:106 225 | msgid "however if your password is too easy to guess," 226 | msgstr "" 227 | 228 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:107 229 | msgid "we will tell you how to make a better one." 230 | msgstr "" 231 | -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/locale/nl/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pierre-Sassoulas/django-zxcvbn-password-validator/960b6ac286a97742465fd4046c16a782c4463830/django_zxcvbn_password_validator/locale/nl/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/locale/nl/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Dutch translations 2 | # Copyright (C) 2018 3 | # This file is distributed under the same license as the django-zxcvbn-password package. 4 | # Thom Wiggers , 2018. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: \n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2025-04-08 05:53+0000\n" 11 | "PO-Revision-Date: 2025-04-09 10:03+0000\n" 12 | "Last-Translator: Michal Čihař \n" 13 | "Language-Team: Dutch \n" 15 | "Language: nl\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 20 | "X-Generator: Weblate 5.11-dev\n" 21 | "X-Translated-Using: django-rosetta 0.9.8\n" 22 | 23 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:24 24 | msgid "Use a few words, avoid common phrases" 25 | msgstr "Gebruik een paar woorden, vermijd veelvoorkomende zinnen" 26 | 27 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:27 28 | msgid "No need for symbols, digits, or uppercase letters" 29 | msgstr "Symbolen, cijfers of hoofdletters zijn niet noodzakelijk" 30 | 31 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:30 32 | msgid "Add another word or two. Uncommon words are better." 33 | msgstr "Voeg nog een woord of twee toe. Ongewone woorden zijn beter." 34 | 35 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:33 36 | msgid "Straight rows of keys are easy to guess" 37 | msgstr "Opeenvolgende toetsen zijn eenvoudig te raden" 38 | 39 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:36 40 | msgid "Short keyboard patterns are easy to guess" 41 | msgstr "Korte patronen van het toetsenbord zijn eenvoudig te raden" 42 | 43 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:39 44 | msgid "Use a longer keyboard pattern with more turns" 45 | msgstr "Gebruik een langer toetsenbordpatroon met meer bochten" 46 | 47 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:42 48 | msgid "Repeats like \"aaa\" are easy to guess" 49 | msgstr "Herhalingen zoals \"aaa\" zijn eenvoudig te raden" 50 | 51 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:45 52 | msgid "" 53 | "Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\"" 54 | msgstr "" 55 | "Herhalingen zoals \"abcabcabc\" zijn maar een klein beetje moeilijker te " 56 | "raden dan \"abc\"" 57 | 58 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:47 59 | msgid "Avoid repeated words and characters" 60 | msgstr "Vermijd herhalingen van woorden en karakters" 61 | 62 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:49 63 | msgid "Sequences like \"abc\" or \"6543\" are easy to guess" 64 | msgstr "Reeksen zoals \"abc\" of \"6543\" zijn eenvoudig te raden" 65 | 66 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:51 67 | msgid "Avoid sequences" 68 | msgstr "Vermijd reeksen" 69 | 70 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:52 71 | msgid "Recent years are easy to guess" 72 | msgstr "Recente jaartallen zijn eenvoudig te raden" 73 | 74 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:53 75 | msgid "Avoid recent years" 76 | msgstr "Vermijd recente jaartallen" 77 | 78 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:55 79 | msgid "Avoid years that are associated with you" 80 | msgstr "Vermijd jaartallen die met jou zijn geassocieerd" 81 | 82 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:57 83 | msgid "Dates are often easy to guess" 84 | msgstr "Data zijn vaak eenvoudig te raden" 85 | 86 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:59 87 | msgid "Avoid dates and years that are associated with you" 88 | msgstr "Vermijd data en jaren die met je zijn geassocieerd" 89 | 90 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:61 91 | msgid "This is a top-10 common password" 92 | msgstr "Dit is een top-10 veelvoorkomend wachtwoord" 93 | 94 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:62 95 | msgid "This is a top-100 common password" 96 | msgstr "Dit is een top-100 veelvoorkomend wachtwoord" 97 | 98 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:63 99 | msgid "This is a very common password" 100 | msgstr "Dit is een veelvoorkomend wachtwoord" 101 | 102 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:65 103 | msgid "This is similar to a commonly used password" 104 | msgstr "Dit lijkt te veel op een veelvoorkomend wachtwoord" 105 | 106 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:67 107 | msgid "A word by itself is easy to guess" 108 | msgstr "Een los woord is eenvoudig te raden" 109 | 110 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:69 111 | msgid "Names and surnames by themselves are easy to guess" 112 | msgstr "Namen en achternamen op zich zijn eenvoudig te raden" 113 | 114 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:72 115 | msgid "Common names and surnames are easy to guess" 116 | msgstr "Veelvoorkomende namen en achternamen zijn eenvoudig te raden" 117 | 118 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:75 119 | msgid "Capitalization doesn't help very much" 120 | msgstr "Hoofdletters toevoegen helpt niet veel" 121 | 122 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:78 123 | msgid "All-uppercase is almost as easy to guess as all-lowercase" 124 | msgstr "" 125 | "Alles in hoofdletters is bijna even eenvoudig te raden als alles in kleine " 126 | "letters" 127 | 128 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:81 129 | msgid "Reversed words aren't much harder to guess" 130 | msgstr "Woorden achterstevoren spellen is niet veel moeilijker te raden" 131 | 132 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:84 133 | msgid "Predictable substitutions like '@' instead of 'a' don't help very much" 134 | msgstr "Voorspelbare vervangingen zoals '@' in plaats van 'a' helpen niet veel" 135 | 136 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:110 137 | msgid "less than a second" 138 | msgstr "minder dan één seconde" 139 | 140 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:113 141 | #, fuzzy, python-format 142 | #| msgid "second" 143 | msgid "%d second" 144 | msgid_plural "%d seconds" 145 | msgstr[0] "seconde" 146 | msgstr[1] "seconde" 147 | 148 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:116 149 | #, fuzzy, python-format 150 | #| msgid "minute" 151 | msgid "%d minute" 152 | msgid_plural "%d minutes" 153 | msgstr[0] "minuut" 154 | msgstr[1] "minuut" 155 | 156 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:119 157 | #, fuzzy, python-format 158 | #| msgid "hour" 159 | msgid "%d hour" 160 | msgid_plural "%d hours" 161 | msgstr[0] "uur" 162 | msgstr[1] "uur" 163 | 164 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:122 165 | #, python-format 166 | msgid "%d day" 167 | msgid_plural "%d days" 168 | msgstr[0] "" 169 | msgstr[1] "" 170 | 171 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:125 172 | #, fuzzy, python-format 173 | #| msgid "month" 174 | msgid "%d month" 175 | msgid_plural "%d months" 176 | msgstr[0] "maand" 177 | msgstr[1] "maand" 178 | 179 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:128 180 | #, fuzzy, python-format 181 | #| msgid "year" 182 | msgid "%d year" 183 | msgid_plural "%d year" 184 | msgstr[0] "jaar" 185 | msgstr[1] "jaar" 186 | 187 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:129 188 | msgid "centuries" 189 | msgstr "eeuwen" 190 | 191 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:71 192 | msgid "Your password exceeds the maximal length of 72 characters." 193 | msgstr "" 194 | 195 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:80 196 | #, fuzzy 197 | #| msgid "Your password is too guessable :" 198 | msgid "Your password is too guessable:" 199 | msgstr "Je wachtwoord is te eenvoudig te raden:" 200 | 201 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:81 202 | #, python-format 203 | msgid "It would take an offline attacker %(time)s to guess it." 204 | msgstr "Het zou een offline aanvaller %(time)s kosten het te raden." 205 | 206 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:86 207 | #, fuzzy 208 | #| msgid "Warning" 209 | msgid "Warning:" 210 | msgstr "Waarschuwing" 211 | 212 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:89 213 | #, fuzzy 214 | #| msgid "Advice" 215 | msgid "Advice:" 216 | msgstr "Advies" 217 | 218 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:95 219 | #, fuzzy 220 | #| msgid "nothing: you can use any password you want." 221 | msgid "We expect nothing: you can use any password you want." 222 | msgstr "niets: je kunt elk wachtwoord dat je wil gebruiken" 223 | 224 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:96 225 | #, fuzzy, python-format 226 | #| msgid "a password that cannot be guessed" 227 | msgid "We expect a password that cannot be guessed %s" 228 | msgstr "een wachtwoord dat niet geraden kan worden" 229 | 230 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:98 231 | msgid "by your familly or friends." 232 | msgstr "door je familie of vrienden." 233 | 234 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:99 235 | msgid "by attackers online." 236 | msgstr "door online aanvallers." 237 | 238 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:100 239 | msgid "without access to our database." 240 | msgstr "zonder toegang tot onze database." 241 | 242 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:101 243 | msgid "without a dedicated team and an access to our database." 244 | msgstr "zonder een toegewijd team en toegang tot onze database." 245 | 246 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:105 247 | msgid "There is no specific rule for a great password," 248 | msgstr "Er is geen specifieke regel voor een goed wachtwoord," 249 | 250 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:106 251 | msgid "however if your password is too easy to guess," 252 | msgstr "maar als je wachtwoord te eenvoudig te raden is," 253 | 254 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:107 255 | msgid "we will tell you how to make a better one." 256 | msgstr "zullen we je vertellen hoe je een beter wachtwoord kunt maken." 257 | 258 | #~ msgid "We expect" 259 | #~ msgstr "We verwachten" 260 | 261 | #~ msgid "seconds" 262 | #~ msgstr "seconden" 263 | 264 | #~ msgid "minutes" 265 | #~ msgstr "minuten" 266 | 267 | #~ msgid "hours" 268 | #~ msgstr "uren" 269 | 270 | #~ msgid "days" 271 | #~ msgstr "dagen" 272 | 273 | #~ msgid "months" 274 | #~ msgstr "maand" 275 | 276 | #~ msgid "years" 277 | #~ msgstr "jaren" 278 | 279 | #~ msgid "day" 280 | #~ msgstr "dag" 281 | -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/locale/pt_BR/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pierre-Sassoulas/django-zxcvbn-password-validator/960b6ac286a97742465fd4046c16a782c4463830/django_zxcvbn_password_validator/locale/pt_BR/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/locale/pt_BR/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Portuguese brasilian translations 2 | # Copyright (C) 2024 3 | # This file is distributed under the same license as the django-zxcvbn-password package. 4 | # Andrés Martano, 2024. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2025-04-08 05:53+0000\n" 12 | "PO-Revision-Date: 2024-05-29 19:41+0000\n" 13 | "Last-Translator: <>\n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 20 | "X-Translated-Using: django-rosetta 0.10.0\n" 21 | 22 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:24 23 | msgid "Use a few words, avoid common phrases" 24 | msgstr "Use algumas palavras, evite palavras geralmente utilizadas em conjunto" 25 | 26 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:27 27 | msgid "No need for symbols, digits, or uppercase letters" 28 | msgstr "Não é necessário utilizar símbolos, números ou letras maiúsculas" 29 | 30 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:30 31 | msgid "Add another word or two. Uncommon words are better." 32 | msgstr "Adicione uma ou duas palavras. Dê preferência palavras pouco usadas." 33 | 34 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:33 35 | msgid "Straight rows of keys are easy to guess" 36 | msgstr "Teclas em sequência são fáceis de adivinhar" 37 | 38 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:36 39 | msgid "Short keyboard patterns are easy to guess" 40 | msgstr "Padrões curtos de teclas são fáceis de adivinhar" 41 | 42 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:39 43 | msgid "Use a longer keyboard pattern with more turns" 44 | msgstr "Use mais teclas e mudando a direção" 45 | 46 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:42 47 | msgid "Repeats like \"aaa\" are easy to guess" 48 | msgstr "Repetições como \"aaa\" são fáceis de adivinhar" 49 | 50 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:45 51 | msgid "" 52 | "Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\"" 53 | msgstr "" 54 | "Repetições como \"abcabcabc\" são apenas um pouco mais difíceis de adivinhar " 55 | "do que \"abc\"" 56 | 57 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:47 58 | msgid "Avoid repeated words and characters" 59 | msgstr "Evite palavras ou caracteres repetidos" 60 | 61 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:49 62 | msgid "Sequences like \"abc\" or \"6543\" are easy to guess" 63 | msgstr "Sequências como \"abc\" ou \"6543\" são fáceis de adivinhar" 64 | 65 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:51 66 | msgid "Avoid sequences" 67 | msgstr "Evite sequências" 68 | 69 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:52 70 | msgid "Recent years are easy to guess" 71 | msgstr "Anos recentes são fáceis de adivinhar" 72 | 73 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:53 74 | msgid "Avoid recent years" 75 | msgstr "Evite anos recentes" 76 | 77 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:55 78 | msgid "Avoid years that are associated with you" 79 | msgstr "Evite anos associados a você" 80 | 81 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:57 82 | msgid "Dates are often easy to guess" 83 | msgstr "Datas geralmente são fáceis de adivinhar" 84 | 85 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:59 86 | msgid "Avoid dates and years that are associated with you" 87 | msgstr "Evite datas e anos associados a você" 88 | 89 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:61 90 | msgid "This is a top-10 common password" 91 | msgstr "Essa é uma das 10 senhas mais comuns" 92 | 93 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:62 94 | msgid "This is a top-100 common password" 95 | msgstr "Essa é uma das 100 senhas mais comuns" 96 | 97 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:63 98 | msgid "This is a very common password" 99 | msgstr "Essa é uma senha muito comum" 100 | 101 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:65 102 | msgid "This is similar to a commonly used password" 103 | msgstr "Essa é similar a uma senha muito comum" 104 | 105 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:67 106 | msgid "A word by itself is easy to guess" 107 | msgstr "Uma única palavra é fácil de adivinhar" 108 | 109 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:69 110 | msgid "Names and surnames by themselves are easy to guess" 111 | msgstr "Nomes e sobrenomes são fáceis de adivinhar" 112 | 113 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:72 114 | msgid "Common names and surnames are easy to guess" 115 | msgstr "Nomes e sobrenomes comuns são fáceis de adivinhar" 116 | 117 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:75 118 | msgid "Capitalization doesn't help very much" 119 | msgstr "Usar maiúsculo só na primeira letra de uma palavra não ajuda muito" 120 | 121 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:78 122 | msgid "All-uppercase is almost as easy to guess as all-lowercase" 123 | msgstr "" 124 | "Usar maiúsculo em todas as letras é quase tão fácil de adivinhar quanto usar " 125 | "só minúsculo" 126 | 127 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:81 128 | msgid "Reversed words aren't much harder to guess" 129 | msgstr "Palavras de trás para frente não são muito mais difíceis de adivinhar" 130 | 131 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:84 132 | msgid "Predictable substitutions like '@' instead of 'a' don't help very much" 133 | msgstr "Substituições previsíveis, como '@' ao invés de 'a', não ajudam muito" 134 | 135 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:110 136 | msgid "less than a second" 137 | msgstr "Menos de um segundo" 138 | 139 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:113 140 | #, fuzzy, python-format 141 | #| msgid "second" 142 | msgid "%d second" 143 | msgid_plural "%d seconds" 144 | msgstr[0] "segundo" 145 | msgstr[1] "segundo" 146 | 147 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:116 148 | #, fuzzy, python-format 149 | #| msgid "minute" 150 | msgid "%d minute" 151 | msgid_plural "%d minutes" 152 | msgstr[0] "minuto" 153 | msgstr[1] "minuto" 154 | 155 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:119 156 | #, fuzzy, python-format 157 | #| msgid "hour" 158 | msgid "%d hour" 159 | msgid_plural "%d hours" 160 | msgstr[0] "hora" 161 | msgstr[1] "hora" 162 | 163 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:122 164 | #, python-format 165 | msgid "%d day" 166 | msgid_plural "%d days" 167 | msgstr[0] "" 168 | msgstr[1] "" 169 | 170 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:125 171 | #, fuzzy, python-format 172 | #| msgid "month" 173 | msgid "%d month" 174 | msgid_plural "%d months" 175 | msgstr[0] "mês" 176 | msgstr[1] "mês" 177 | 178 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:128 179 | #, fuzzy, python-format 180 | #| msgid "year" 181 | msgid "%d year" 182 | msgid_plural "%d year" 183 | msgstr[0] "ano" 184 | msgstr[1] "ano" 185 | 186 | #: django_zxcvbn_password_validator/translate_zxcvbn_text.py:129 187 | msgid "centuries" 188 | msgstr "séculos" 189 | 190 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:71 191 | msgid "Your password exceeds the maximal length of 72 characters." 192 | msgstr "" 193 | 194 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:80 195 | #, fuzzy 196 | #| msgid "Your password is too guessable :" 197 | msgid "Your password is too guessable:" 198 | msgstr "Sua senha é muito fácil de adivinhar :" 199 | 200 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:81 201 | #, python-format 202 | msgid "It would take an offline attacker %(time)s to guess it." 203 | msgstr "Um ataque offline levaria %(time)s para adivinhá-la." 204 | 205 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:86 206 | #, fuzzy 207 | #| msgid "Warning" 208 | msgid "Warning:" 209 | msgstr "Aviso" 210 | 211 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:89 212 | #, fuzzy 213 | #| msgid "Advice" 214 | msgid "Advice:" 215 | msgstr "Dica" 216 | 217 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:95 218 | #, fuzzy 219 | #| msgid "nothing: you can use any password you want." 220 | msgid "We expect nothing: you can use any password you want." 221 | msgstr "nenhuma: você pode usar qualquer senha que quiser." 222 | 223 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:96 224 | #, fuzzy, python-format 225 | #| msgid "a password that cannot be guessed" 226 | msgid "We expect a password that cannot be guessed %s" 227 | msgstr "uma senha que não possa ser adivinhada" 228 | 229 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:98 230 | msgid "by your familly or friends." 231 | msgstr "por sua família ou amigos." 232 | 233 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:99 234 | msgid "by attackers online." 235 | msgstr "por atacantes online." 236 | 237 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:100 238 | msgid "without access to our database." 239 | msgstr "sem acesso à nossa base de dados." 240 | 241 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:101 242 | msgid "without a dedicated team and an access to our database." 243 | msgstr "sem uma equipe dedicada e com acesso à nossa base de dados." 244 | 245 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:105 246 | msgid "There is no specific rule for a great password," 247 | msgstr "Não existe uma regra específica para uma boa senha," 248 | 249 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:106 250 | msgid "however if your password is too easy to guess," 251 | msgstr "porém se sua senha for muito fácil de adivinhar," 252 | 253 | #: django_zxcvbn_password_validator/zxcvbn_password_validator.py:107 254 | msgid "we will tell you how to make a better one." 255 | msgstr "nós daremos sugestões de como melhorá-la." 256 | 257 | #~ msgid "We expect" 258 | #~ msgstr "Nós estimamos" 259 | 260 | #~ msgid "seconds" 261 | #~ msgstr "segundos" 262 | 263 | #~ msgid "minutes" 264 | #~ msgstr "minutos" 265 | 266 | #~ msgid "hours" 267 | #~ msgstr "horas" 268 | 269 | #~ msgid "days" 270 | #~ msgstr "dias" 271 | 272 | #~ msgid "months" 273 | #~ msgstr "meses" 274 | 275 | #~ msgid "years" 276 | #~ msgstr "anos" 277 | 278 | #~ msgid "day" 279 | #~ msgstr "dia" 280 | -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/settings.py: -------------------------------------------------------------------------------- 1 | DEFAULT_MINIMAL_STRENGTH = 2 2 | -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/test_translate_zxcvbn_text.py: -------------------------------------------------------------------------------- 1 | # mypy: ignore-errors 2 | 3 | from django.test import TestCase, override_settings 4 | 5 | from django_zxcvbn_password_validator.translate_zxcvbn_text import translate_zxcvbn_text 6 | 7 | 8 | class TranslateZxcvbnTextTest(TestCase): 9 | @override_settings(LANGUAGE_CODE="en-us") 10 | def test_help_text(self): 11 | test = "Disregard this logging text :)." 12 | self.assertEqual(translate_zxcvbn_text(test), test) 13 | 14 | @override_settings(LANGUAGE_CODE="fr") 15 | def test_help_text_i18n(self): 16 | self.assertEqual( 17 | translate_zxcvbn_text("Use a few words, avoid common phrases"), 18 | "Utilisez quelques mots, évitez les mots souvent utilisés ensemble", 19 | ) 20 | -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/test_zxcvbn_password_validator.py: -------------------------------------------------------------------------------- 1 | # mypy: ignore-errors 2 | 3 | from django.conf import settings 4 | from django.contrib.auth.models import User 5 | from django.contrib.auth.password_validation import get_default_password_validators 6 | from django.core.exceptions import ImproperlyConfigured, ValidationError 7 | from django.test import TestCase, override_settings 8 | 9 | from django_zxcvbn_password_validator import ( 10 | DEFAULT_MINIMAL_STRENGTH, 11 | ZxcvbnPasswordValidator, 12 | ) 13 | 14 | 15 | @override_settings( 16 | AUTH_PASSWORD_VALIDATORS=[ 17 | {"NAME": "django_zxcvbn_password_validator.ZxcvbnPasswordValidator"} 18 | ] 19 | ) 20 | class ZxcvbnPasswordValidatorTest(TestCase): 21 | def setUp(self): 22 | self.maxDiff = None # pylint: disable=invalid-name 23 | self.validator = get_default_password_validators()[0] 24 | 25 | def test_settings_not_set(self): 26 | del settings.PASSWORD_MINIMAL_STRENGTH 27 | self.validator = ZxcvbnPasswordValidator() 28 | self.assertEqual( 29 | self.validator.password_minimal_strength, DEFAULT_MINIMAL_STRENGTH 30 | ) 31 | 32 | @override_settings(PASSWORD_MINIMAL_STRENGTH="4") 33 | def test_settings_not_int(self): 34 | with self.assertRaises(ImproperlyConfigured) as error: 35 | self.validator = ZxcvbnPasswordValidator() 36 | self.assertIn("need an integer between 0 and 4", str(error.exception)) 37 | self.assertIn("(not '4', a str)", str(error.exception)) 38 | 39 | @override_settings(PASSWORD_MINIMAL_STRENGTH="foor") 40 | def test_absurd_settings(self): 41 | with self.assertRaises(ImproperlyConfigured) as error: 42 | self.validator = ZxcvbnPasswordValidator() 43 | self.assertIn("need an integer between 0 and 4", str(error.exception)) 44 | self.assertIn("(not 'foor', a str)", str(error.exception)) 45 | 46 | @override_settings(PASSWORD_MINIMAL_STRENGTH=5) 47 | def test_settings_not_in_range_high(self): 48 | with self.assertRaises(ImproperlyConfigured) as error: 49 | self.validator = ZxcvbnPasswordValidator() 50 | self.assertIn("need an integer between 0 and 4", str(error.exception)) 51 | 52 | @override_settings(PASSWORD_MINIMAL_STRENGTH=-1) 53 | def test_settings_not_in_range_low(self): 54 | with self.assertRaises(ImproperlyConfigured) as error: 55 | self.validator = ZxcvbnPasswordValidator() 56 | self.assertIn("need an integer between 0 and 4", str(error.exception)) 57 | 58 | @override_settings(LANGUAGE_CODE="en-us") 59 | def test_validate_too_short(self): 60 | with self.assertRaises(ValidationError) as error: 61 | self.validator.validate("d@1sR") 62 | self.assertIn("Add another word or two.", str(error.exception)) 63 | with self.assertRaises(ValidationError) as error: 64 | self.validator.validate("d5G=}78") 65 | self.assertIn("Add another word or two.", str(error.exception)) 66 | 67 | @override_settings(LANGUAGE_CODE="en-us") 68 | @override_settings(PASSWORD_MINIMAL_STRENGTH=4) 69 | def test_validate_user_similarity(self): 70 | user = User.objects.create( 71 | username="testclient", 72 | first_name="Test", 73 | last_name="Client", 74 | email="testclient@example.com", 75 | password="sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161", 76 | ) 77 | self.assertIsNone(self.validator.validate("testclient@example.com")) 78 | 79 | with self.assertRaises(ValidationError) as error: 80 | self.validator.validate("testclient@example.com", user=user) 81 | self.assertIn("Your password is too guessable", error.exception.messages[0]) 82 | 83 | @override_settings(PASSWORD_MINIMAL_STRENGTH=0) 84 | def test_low_strength(self): 85 | self.validator = ZxcvbnPasswordValidator() 86 | self.assertIsNone(self.validator.validate("password")) 87 | self.assertIsNone(self.validator.validate("123")) 88 | self.assertIsNone(self.validator.validate("godzilla")) 89 | 90 | @override_settings(LANGUAGE_CODE="en-us") 91 | def test_long_password(self): 92 | self.validator = ZxcvbnPasswordValidator() 93 | with self.assertRaises(ValidationError) as error: 94 | self.validator.validate("x" * 80) 95 | self.assertIn( 96 | "Your password exceeds the maximal length", error.exception.messages[0] 97 | ) 98 | 99 | @override_settings(PASSWORD_MINIMAL_STRENGTH=None) 100 | @override_settings(PASSWORD_MINIMAL_STRENTH=0) 101 | def test_compatibility_with_old_settings(self): 102 | """'PASSWORD_MINIMAL_STRENTH' with a typo is still working. 103 | 104 | If we're using the default value of 2 instead of taking the value 105 | from 'PASSWORD_MINIMAL_STRENTH' into account then 'password' 106 | would not be strong enough. 107 | """ 108 | self.validator = ZxcvbnPasswordValidator() 109 | self.assertIsNone(self.validator.validate("password")) 110 | 111 | @override_settings(PASSWORD_MINIMAL_STRENGTH=0) 112 | @override_settings(PASSWORD_MINIMAL_STRENTH=2) 113 | def test_priority_of_new_settings(self): 114 | """We need to give priority to the new value when we have both settings.""" 115 | self.validator = ZxcvbnPasswordValidator() 116 | self.assertIsNone(self.validator.validate("password")) 117 | 118 | @override_settings(PASSWORD_MINIMAL_STRENGTH=2) 119 | @override_settings(LANGUAGE_CODE="en-us") 120 | def test_medium_strength(self): 121 | self.validator = ZxcvbnPasswordValidator() 122 | with self.assertRaises(ValidationError) as error: 123 | self.assertIsNone(self.validator.validate("p@sswOrd1")) 124 | self.assertIn("Your password is too guessable", error.exception.messages[0]) 125 | self.assertIn("Predictable substitutions", str(error.exception)) 126 | with self.assertRaises(ValidationError) as error: 127 | self.assertIsNone(self.validator.validate("123123123123")) 128 | self.assertIn("Your password is too guessable", error.exception.messages[0]) 129 | self.assertIn('Repeats like "abcabcabc"', str(error.exception)) 130 | with self.assertRaises(ValidationError) as error: 131 | self.assertIsNone(self.validator.validate("g0dz1ll@")) 132 | self.assertIn("Your password is too guessable", error.exception.messages[0]) 133 | self.assertIn( 134 | "This is similar to a commonly used password", str(error.exception) 135 | ) 136 | 137 | @override_settings(PASSWORD_MINIMAL_STRENGTH=4) 138 | @override_settings(LANGUAGE_CODE="fr") 139 | def test_strength_i18n(self): 140 | self.validator = ZxcvbnPasswordValidator() 141 | with self.assertRaises(ValidationError) as error: 142 | self.assertIsNone(self.validator.validate("g0dz1ll@")) 143 | self.assertIn( 144 | "Votre mot de passe est trop facile à deviner", error.exception.messages[0] 145 | ) 146 | self.assertIn( 147 | "C'est proche d'un mot de passe très courant", str(error.exception) 148 | ) 149 | self.assertIn( 150 | "Le deviner prendrait moins d'une seconde à un attaquant", 151 | str(error.exception), 152 | ) 153 | with self.assertRaises(ValidationError) as error: 154 | self.assertIsNone(self.validator.validate("g0dze1ll@")) 155 | self.assertIn("prendrait 12 heures à un attaquant", str(error.exception)) 156 | with self.assertRaises(ValidationError) as error: 157 | self.assertIsNone(self.validator.validate("g0de1ll@")) 158 | self.assertIn("prendrait 1 heure à un attaquant", str(error.exception)) 159 | 160 | @override_settings(PASSWORD_MINIMAL_STRENGTH=4) 161 | def test_high_strength(self): 162 | self.validator = ZxcvbnPasswordValidator() 163 | self.assertRaises(ValidationError, self.validator.validate, "password") 164 | self.assertRaises(ValidationError, self.validator.validate, "123") 165 | self.assertRaises(ValidationError, self.validator.validate, "godzilla") 166 | self.assertIsNone( 167 | self.validator.validate("Ho, you want a password ? This is a password :@") 168 | ) 169 | self.assertIsNone( 170 | self.validator.validate( 171 | "123 je m'en vais au bois, 456 cueillir des cerises :)" 172 | ) 173 | ) 174 | self.assertIsNone( 175 | self.validator.validate("A God, an alpha predator, Godzilla.") 176 | ) 177 | 178 | @override_settings(LANGUAGE_CODE="en-us") 179 | def test_help_text(self): 180 | self.assertEqual( 181 | self.validator.get_help_text(), 182 | "There is no specific rule for a great password, however if your" 183 | " password is too easy to guess, we will tell you how to make a " 184 | "better one. We expect a password that cannot be guessed without" 185 | " access to our database.", 186 | ) 187 | 188 | @override_settings(LANGUAGE_CODE="en-us") 189 | @override_settings(PASSWORD_MINIMAL_STRENGTH=0) 190 | def test_help_text_accept_all(self): 191 | self.validator = ZxcvbnPasswordValidator() 192 | self.assertEqual( 193 | self.validator.get_help_text(), 194 | "We expect nothing: you can use any password you want.", 195 | ) 196 | 197 | @override_settings(LANGUAGE_CODE="en-us") 198 | @override_settings(PASSWORD_MINIMAL_STRENGTH=1) 199 | def test_help_text_low_strength(self): 200 | self.validator = ZxcvbnPasswordValidator() 201 | self.assertEqual( 202 | self.validator.get_help_text(), 203 | "There is no specific rule for a great password, however if your" 204 | " password is too easy to guess, we will tell you how to make a " 205 | "better one. We expect a password that cannot be guessed by your" 206 | " familly or friends.", 207 | ) 208 | 209 | @override_settings(LANGUAGE_CODE="fr") 210 | @override_settings(PASSWORD_MINIMAL_STRENGTH=2) 211 | def test_help_text_i18n(self): 212 | self.validator = ZxcvbnPasswordValidator() 213 | self.assertEqual( 214 | self.validator.get_help_text(), 215 | "Il n'y a pas de règle absolue pour un bon mot de passe, " 216 | "cependant si votre mot de passe est trop facile à deviner," 217 | " nous vous dirons comment l'améliorer. Nous demandons un " 218 | "mot de passe qui ne peut pas être deviné par des attaquants" 219 | " sur internet.", 220 | ) 221 | -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/translate_zxcvbn_text.py: -------------------------------------------------------------------------------- 1 | # mypy: ignore-errors 2 | 3 | import logging 4 | from decimal import Decimal 5 | 6 | try: 7 | from django.utils.translation import gettext_lazy as _ 8 | from django.utils.translation import ngettext 9 | except ImportError: 10 | from django.utils.translation import ngettext 11 | from django.utils.translation import ugettext_lazy as _ 12 | 13 | LOGGER = logging.getLogger(__file__) 14 | 15 | 16 | def translate_zxcvbn_text(text): 17 | """Translate text using our own i18n dict. 18 | 19 | https://github.com/dropbox/zxcvbn/pull/124#issuecomment-430081232 would have 20 | made this cleaner. 21 | """ 22 | i18n = { 23 | "Use a few words, avoid common phrases": _( 24 | "Use a few words, avoid common phrases" 25 | ), 26 | "No need for symbols, digits, or uppercase letters": _( 27 | "No need for symbols, digits, or uppercase letters" 28 | ), 29 | "Add another word or two. Uncommon words are better.": _( 30 | "Add another word or two. Uncommon words are better." 31 | ), 32 | "Straight rows of keys are easy to guess": _( 33 | "Straight rows of keys are easy to guess" 34 | ), 35 | "Short keyboard patterns are easy to guess": _( 36 | "Short keyboard patterns are easy to guess" 37 | ), 38 | "Use a longer keyboard pattern with more turns": _( 39 | "Use a longer keyboard pattern with more turns" 40 | ), 41 | 'Repeats like "aaa" are easy to guess': _( 42 | 'Repeats like "aaa" are easy to guess' 43 | ), 44 | 'Repeats like "abcabcabc" are only slightly harder to guess than "abc"': _( 45 | 'Repeats like "abcabcabc" are only slightly harder to guess than "abc"' 46 | ), 47 | "Avoid repeated words and characters": _("Avoid repeated words and characters"), 48 | 'Sequences like "abc" or "6543" are easy to guess': _( 49 | 'Sequences like "abc" or "6543" are easy to guess' 50 | ), 51 | "Avoid sequences": _("Avoid sequences"), 52 | "Recent years are easy to guess": _("Recent years are easy to guess"), 53 | "Avoid recent years": _("Avoid recent years"), 54 | "Avoid years that are associated with you": _( 55 | "Avoid years that are associated with you" 56 | ), 57 | "Dates are often easy to guess": _("Dates are often easy to guess"), 58 | "Avoid dates and years that are associated with you": _( 59 | "Avoid dates and years that are associated with you" 60 | ), 61 | "This is a top-10 common password": _("This is a top-10 common password"), 62 | "This is a top-100 common password": _("This is a top-100 common password"), 63 | "This is a very common password": _("This is a very common password"), 64 | "This is similar to a commonly used password": _( 65 | "This is similar to a commonly used password" 66 | ), 67 | "A word by itself is easy to guess": _("A word by itself is easy to guess"), 68 | "Names and surnames by themselves are easy to guess": _( 69 | "Names and surnames by themselves are easy to guess" 70 | ), 71 | "Common names and surnames are easy to guess": _( 72 | "Common names and surnames are easy to guess" 73 | ), 74 | "Capitalization doesn't help very much": _( 75 | "Capitalization doesn't help very much" 76 | ), 77 | "All-uppercase is almost as easy to guess as all-lowercase": _( 78 | "All-uppercase is almost as easy to guess as all-lowercase" 79 | ), 80 | "Reversed words aren't much harder to guess": _( 81 | "Reversed words aren't much harder to guess" 82 | ), 83 | "Predictable substitutions like '@' instead of 'a' don't help very much": _( 84 | "Predictable substitutions like '@' instead of 'a' don't help very much" 85 | ), 86 | } 87 | translated_text = i18n.get(text) 88 | if translated_text is None: 89 | # zxcvbn is inconsistent, sometime there is a dot, sometime not 90 | translated_text = i18n.get(text[:-1]) 91 | if translated_text is None: 92 | LOGGER.warning( 93 | "No translation for '%s' or '%s', update the generatei18ndict command.", 94 | text, 95 | text[:-1], 96 | ) 97 | return text 98 | return translated_text 99 | 100 | 101 | def translate_zxcvbn_time_estimate(seconds: Decimal) -> str: # noqa: PLR0911 # pylint: disable=R0911 102 | """Based on display_time from zxcvbn.""" 103 | minute = 60 104 | hour = minute * 60 105 | day = hour * 24 106 | month = day * 31 107 | year = month * 12 108 | century = year * 100 109 | if seconds < 1: 110 | return _("less than a second") 111 | if seconds < minute: 112 | base = round(seconds) 113 | return ngettext("%d second", "%d seconds", base) % base 114 | if seconds < hour: 115 | base = round(seconds / minute) 116 | return ngettext("%d minute", "%d minutes", base) % base 117 | if seconds < day: 118 | base = round(seconds / hour) 119 | return ngettext("%d hour", "%d hours", base) % base 120 | if seconds < month: 121 | base = round(seconds / day) 122 | return ngettext("%d day", "%d days", base) % base 123 | if seconds < year: 124 | base = round(seconds / month) 125 | return ngettext("%d month", "%d months", base) % base 126 | if seconds < century: 127 | base = round(seconds / year) 128 | return ngettext("%d year", "%d year", base) % base 129 | return _("centuries") 130 | -------------------------------------------------------------------------------- /django_zxcvbn_password_validator/zxcvbn_password_validator.py: -------------------------------------------------------------------------------- 1 | # mypy: ignore-errors 2 | 3 | from django.conf import settings 4 | from django.core.exceptions import ImproperlyConfigured, ValidationError 5 | 6 | try: 7 | from django.utils.translation import gettext_lazy as _ 8 | except ImportError: 9 | from django.utils.translation import ugettext_lazy as _ 10 | 11 | from zxcvbn import zxcvbn 12 | 13 | from django_zxcvbn_password_validator.settings import DEFAULT_MINIMAL_STRENGTH 14 | from django_zxcvbn_password_validator.translate_zxcvbn_text import ( 15 | translate_zxcvbn_text, 16 | translate_zxcvbn_time_estimate, 17 | ) 18 | 19 | 20 | class ZxcvbnPasswordValidator: 21 | def __init__(self, min_length=1, zxcvbn_implementation=zxcvbn): 22 | self.min_length = min_length 23 | self.zxcvbn_implementation = zxcvbn_implementation 24 | password_minimal_strength = getattr(settings, "PASSWORD_MINIMAL_STRENGTH", None) 25 | if password_minimal_strength is None: 26 | # Compatibility with a typo in previous version. 27 | password_minimal_strength = getattr( 28 | settings, "PASSWORD_MINIMAL_STRENTH", None 29 | ) 30 | if password_minimal_strength is None: 31 | password_minimal_strength = DEFAULT_MINIMAL_STRENGTH 32 | self.password_minimal_strength = password_minimal_strength 33 | self.__check_password_minimal_strength() 34 | 35 | def __check_password_minimal_strength(self): 36 | error_msg = "ZxcvbnPasswordValidator need an integer between 0 and 4 " 37 | error_msg += "for PASSWORD_MINIMAL_STRENGTH in the settings." 38 | try: 39 | not_an_int = ( 40 | int(self.password_minimal_strength) != self.password_minimal_strength 41 | ) 42 | except ValueError: 43 | not_an_int = True 44 | if not_an_int: 45 | error_msg += f" (not '{self.password_minimal_strength}', " 46 | error_msg += f"a {self.password_minimal_strength.__class__.__name__})" 47 | raise ImproperlyConfigured(error_msg) 48 | if self.password_minimal_strength < 0 or self.password_minimal_strength > 4: 49 | error_msg += f" ({self.password_minimal_strength} is not in [0,4])" 50 | raise ImproperlyConfigured(error_msg) 51 | 52 | def validate(self, password, user=None): 53 | def append_translated_feedback(old_feedbacks, feedback_type, new_feedbacks): 54 | if new_feedbacks: 55 | if isinstance(new_feedbacks, str): 56 | new_feedbacks = [new_feedbacks] 57 | for new_feedback in new_feedbacks: 58 | old_feedbacks.append( 59 | f"{feedback_type} {translate_zxcvbn_text(new_feedback)}" 60 | ) 61 | 62 | user_inputs = [] 63 | if user: 64 | for value in user.__dict__.values(): 65 | user_inputs.append(value) 66 | try: 67 | results = self.zxcvbn_implementation(password, user_inputs=user_inputs) 68 | except ValueError as error: 69 | # zxcvbn raises ValueError only if the password is too long. 70 | raise ValidationError( 71 | _("Your password exceeds the maximal length of 72 characters.") 72 | ) from error 73 | password_strength = results["score"] 74 | if password_strength < self.password_minimal_strength: 75 | crack_time = results["crack_times_seconds"] 76 | offline_time = crack_time["offline_slow_hashing_1e4_per_second"] 77 | 78 | feedbacks = [ 79 | "{} {}".format( # pylint: disable=consider-using-f-string 80 | _("Your password is too guessable:"), 81 | _("It would take an offline attacker %(time)s to guess it.") 82 | % {"time": translate_zxcvbn_time_estimate(offline_time)}, 83 | ) 84 | ] 85 | append_translated_feedback( 86 | feedbacks, _("Warning:"), results["feedback"]["warning"] 87 | ) 88 | append_translated_feedback( 89 | feedbacks, _("Advice:"), results["feedback"]["suggestions"] 90 | ) 91 | raise ValidationError(feedbacks) 92 | 93 | def get_help_text(self): 94 | if self.password_minimal_strength == 0: 95 | return _("We expect nothing: you can use any password you want.") 96 | expectations = _("We expect a password that cannot be guessed %s") 97 | hardness = { 98 | 1: _("by your familly or friends."), 99 | 2: _("by attackers online."), 100 | 3: _("without access to our database."), 101 | 4: _("without a dedicated team and an access to our database."), 102 | } 103 | # pylint: disable=consider-using-f-string 104 | return "{} {} {} {}".format( 105 | _("There is no specific rule for a great password,"), 106 | _("however if your password is too easy to guess,"), 107 | _("we will tell you how to make a better one."), 108 | expectations % hardness.get(self.password_minimal_strength), 109 | ) 110 | -------------------------------------------------------------------------------- /doc/english_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pierre-Sassoulas/django-zxcvbn-password-validator/960b6ac286a97742465fd4046c16a782c4463830/doc/english_example.png -------------------------------------------------------------------------------- /doc/french_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pierre-Sassoulas/django-zxcvbn-password-validator/960b6ac286a97742465fd4046c16a782c4463830/doc/french_example.png -------------------------------------------------------------------------------- /example_project/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | -------------------------------------------------------------------------------- /example_project/example/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pierre-Sassoulas/django-zxcvbn-password-validator/960b6ac286a97742465fd4046c16a782c4463830/example_project/example/__init__.py -------------------------------------------------------------------------------- /example_project/example/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for example project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.0.6. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.0/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = "6+z)mwml!z6#xlb#(3w6-z#xu$9n^0@*7g2#h5hs@q5p+!ji0f" 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | "django.contrib.admin", 35 | "django.contrib.auth", 36 | "django.contrib.contenttypes", 37 | "django.contrib.sessions", 38 | "django.contrib.messages", 39 | "django.contrib.staticfiles", 40 | "django_zxcvbn_password_validator", 41 | ] 42 | 43 | MIDDLEWARE = [ 44 | "django.middleware.security.SecurityMiddleware", 45 | "django.contrib.sessions.middleware.SessionMiddleware", 46 | "django.middleware.common.CommonMiddleware", 47 | "django.middleware.csrf.CsrfViewMiddleware", 48 | "django.contrib.auth.middleware.AuthenticationMiddleware", 49 | "django.contrib.messages.middleware.MessageMiddleware", 50 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 51 | ] 52 | 53 | ROOT_URLCONF = "example.urls" 54 | 55 | TEMPLATES = [ 56 | { 57 | "BACKEND": "django.template.backends.django.DjangoTemplates", 58 | "DIRS": [], 59 | "APP_DIRS": True, 60 | "OPTIONS": { 61 | "context_processors": [ 62 | "django.template.context_processors.debug", 63 | "django.template.context_processors.request", 64 | "django.contrib.auth.context_processors.auth", 65 | "django.contrib.messages.context_processors.messages", 66 | ] 67 | }, 68 | } 69 | ] 70 | 71 | WSGI_APPLICATION = "example.wsgi.application" 72 | 73 | 74 | # Database 75 | # https://docs.djangoproject.com/en/2.0/ref/settings/#databases 76 | 77 | DATABASES = { 78 | "default": { 79 | "ENGINE": "django.db.backends.sqlite3", 80 | "NAME": os.path.join(BASE_DIR, "db.sqlite3"), 81 | } 82 | } 83 | 84 | 85 | # Password validation 86 | # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators 87 | 88 | AUTH_PASSWORD_VALIDATORS = [ 89 | { 90 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" 91 | }, 92 | {"NAME": "django_zxcvbn_password_validator.ZxcvbnPasswordValidator"}, 93 | ] 94 | 95 | 96 | # Internationalization 97 | # https://docs.djangoproject.com/en/2.0/topics/i18n/ 98 | 99 | LANGUAGE_CODE = "fr" 100 | 101 | TIME_ZONE = "UTC" 102 | 103 | USE_I18N = True 104 | 105 | USE_L10N = True 106 | 107 | USE_TZ = True 108 | 109 | 110 | # Static files (CSS, JavaScript, Images) 111 | # https://docs.djangoproject.com/en/2.0/howto/static-files/ 112 | 113 | STATIC_URL = "/static/" 114 | -------------------------------------------------------------------------------- /example_project/example/urls.py: -------------------------------------------------------------------------------- 1 | """example URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.0/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | 17 | from django.contrib import admin 18 | from django.urls import path 19 | 20 | urlpatterns = [path("admin/", admin.site.urls)] 21 | -------------------------------------------------------------------------------- /example_project/example/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for example project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /example_project/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /example_project/requirements.txt: -------------------------------------------------------------------------------- 1 | django-zxcvbn-password-validator 2 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "translation_project.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as e: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from e 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | build-backend = "setuptools.build_meta" 3 | 4 | requires = [ 5 | "setuptools>=67.5.1", 6 | "wheel", 7 | ] 8 | 9 | [project] 10 | name = "django-zxcvbn-password-validator" 11 | version = "1.4.6" 12 | description = "A translatable password validator for django, based on zxcvbn-python." 13 | readme = "README.md" 14 | keywords = [ 15 | "django", 16 | "password-validator", 17 | "zxcvbn", 18 | ] 19 | license = { text = "MIT" } 20 | authors = [ 21 | { email = "pierre.sassoulas@gmail.com", name = "Pierre SASSOULAS" }, 22 | ] 23 | requires-python = ">=3.9.0" 24 | 25 | classifiers = [ 26 | "Development Status :: 5 - Production/Stable", 27 | "Framework :: Django", 28 | "Intended Audience :: Developers", 29 | "License :: OSI Approved :: MIT License", 30 | "Natural Language :: Dutch", 31 | "Natural Language :: English", 32 | "Natural Language :: French", 33 | "Natural Language :: Portuguese (Brazilian)", 34 | "Operating System :: OS Independent", 35 | "Programming Language :: Python :: 3 :: Only", 36 | "Programming Language :: Python :: 3.9", 37 | "Programming Language :: Python :: 3.10", 38 | "Programming Language :: Python :: 3.11", 39 | "Programming Language :: Python :: 3.12", 40 | "Programming Language :: Python :: 3.13", 41 | ] 42 | dependencies = [ 43 | "django>=2,<6", 44 | "zxcvbn", 45 | ] 46 | optional-dependencies.dev = [ 47 | "build", 48 | "coverage", 49 | "coveralls", 50 | "django-rosetta", 51 | "mock", 52 | "python-coveralls", 53 | ] 54 | optional-dependencies.pylint = [ 55 | "pre-commit>=2.16", 56 | "pylint>=3.2", 57 | "pylint-django==2.5.5", 58 | ] 59 | urls."Bug Tracker" = "https://github.com/Pierre-Sassoulas/django-zxcvbn-password-validator/issues" 60 | urls."Source Code" = "https://github.com/Pierre-Sassoulas/django-zxcvbn-password-validator" 61 | 62 | [tool.setuptools.packages.find] 63 | include = [ 64 | "django_zxcvbn_password_validator*", 65 | ] 66 | 67 | [tool.ruff] 68 | 69 | line-length = 88 70 | lint.select = [ 71 | "B", # bugbear 72 | "D", # pydocstyle 73 | "E", # pycodestyle 74 | "F", # pyflakes 75 | "I", # isort 76 | "PGH004", # pygrep-hooks - Use specific rule codes when using noqa 77 | "PIE", # flake8-pie 78 | "PL", # pylint 79 | "PYI", # flake8-pyi 80 | "RUF", # ruff 81 | "T100", # flake8-debugger 82 | "UP", # pyupgrade 83 | "W", # pycodestyle 84 | ] 85 | 86 | lint.ignore = [ 87 | # pydocstyle ignore 88 | "D100", # Missing docstring in public module 89 | "D101", # Missing docstring in public class 90 | "D102", # Missing docstring in public method 91 | "D103", # Missing docstring in public function 92 | "D105", # Missing docstring in magic method 93 | "D106", # Missing docstring in public nested class 94 | "D107", # Missing docstring in `__init__` 95 | "D203", # one-blank-line-before-class` 96 | # pylint ignore 97 | "PLR2004", # Magic value used in comparison 98 | ] 99 | 100 | [tool.pylint.main] 101 | 102 | load-plugins = "pylint_django" 103 | django-settings-module = "django_zxcvbn_password_validator.settings" 104 | disable = [ 105 | # I0011 Warning locally suppressed using disable-msg 106 | "I0011", 107 | "missing-docstring", # We don't want docstring everywhere 108 | "imported-auth-user", # harmful according to django's dev 109 | ] 110 | 111 | max-line-length = 88 112 | -------------------------------------------------------------------------------- /translation_project/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pierre-Sassoulas/django-zxcvbn-password-validator/960b6ac286a97742465fd4046c16a782c4463830/translation_project/__init__.py -------------------------------------------------------------------------------- /translation_project/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pierre-Sassoulas/django-zxcvbn-password-validator/960b6ac286a97742465fd4046c16a782c4463830/translation_project/management/__init__.py -------------------------------------------------------------------------------- /translation_project/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pierre-Sassoulas/django-zxcvbn-password-validator/960b6ac286a97742465fd4046c16a782c4463830/translation_project/management/commands/__init__.py -------------------------------------------------------------------------------- /translation_project/management/commands/generatei18ndict.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | 3 | 4 | class Command(BaseCommand): 5 | help = "Will generate what the i18n dict for the translate_zxcvbn_text function" 6 | 7 | def handle(self, *args, **options): 8 | existings_messages = [ 9 | "Use a few words, avoid common phrases", 10 | "No need for symbols, digits, or uppercase letters", 11 | "Add another word or two. Uncommon words are better.", 12 | "Straight rows of keys are easy to guess", 13 | "Short keyboard patterns are easy to guess", 14 | "Use a longer keyboard pattern with more turns", 15 | 'Repeats like "aaa" are easy to guess', 16 | 'Repeats like "abcabcabc" are only slightly harder to guess than "abc"', 17 | "Avoid repeated words and characters", 18 | 'Sequences like "abc" or "6543" are easy to guess', 19 | "Avoid sequences", 20 | "Recent years are easy to guess", 21 | "Avoid recent years", 22 | "Avoid years that are associated with you", 23 | "Dates are often easy to guess", 24 | "Avoid dates and years that are associated with you", 25 | "This is a top-10 common password", 26 | "This is a top-100 common password", 27 | "This is a very common password", 28 | "This is similar to a commonly used password", 29 | "A word by itself is easy to guess", 30 | "Names and surnames by themselves are easy to guess", 31 | "Common names and surnames are easy to guess", 32 | "Capitalization doesn't help very much", 33 | "All-uppercase is almost as easy to guess as all-lowercase", 34 | "Reversed words aren't much harder to guess", 35 | "Predictable substitutions like '@' instead of 'a' don't help very much", 36 | ] 37 | msg = " i18n = {" 38 | for message in existings_messages: 39 | message = message.replace("'", "\\'") 40 | msg += f" '{message}': _('{message}')," 41 | msg += " }" 42 | msg += "Please copy paste the following in the translate_zxcvbn_text function," 43 | msg += " then use 'python manage.py makemessages'." 44 | print(msg) 45 | -------------------------------------------------------------------------------- /translation_project/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for translation_project project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.0.6. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.0/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = "@mdzmc@gjxecs2kais)*0-@hv*n-pb6$b^d#91ia5#)t##k^99" 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | "django.contrib.admin", 35 | "django.contrib.auth", 36 | "django.contrib.contenttypes", 37 | "django.contrib.sessions", 38 | "django.contrib.messages", 39 | "django.contrib.staticfiles", 40 | "django_zxcvbn_password_validator", 41 | "rosetta", 42 | "translation_project", 43 | ] 44 | 45 | MIDDLEWARE = [ 46 | "django.middleware.security.SecurityMiddleware", 47 | "django.contrib.sessions.middleware.SessionMiddleware", 48 | "django.middleware.common.CommonMiddleware", 49 | "django.middleware.csrf.CsrfViewMiddleware", 50 | "django.contrib.auth.middleware.AuthenticationMiddleware", 51 | "django.contrib.messages.middleware.MessageMiddleware", 52 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 53 | ] 54 | 55 | ROOT_URLCONF = "translation_project.urls" 56 | 57 | TEMPLATES = [ 58 | { 59 | "BACKEND": "django.template.backends.django.DjangoTemplates", 60 | "DIRS": [], 61 | "APP_DIRS": True, 62 | "OPTIONS": { 63 | "context_processors": [ 64 | "django.template.context_processors.debug", 65 | "django.template.context_processors.request", 66 | "django.contrib.auth.context_processors.auth", 67 | "django.contrib.messages.context_processors.messages", 68 | ] 69 | }, 70 | } 71 | ] 72 | 73 | WSGI_APPLICATION = "translation_project.wsgi.application" 74 | 75 | 76 | # Database 77 | # https://docs.djangoproject.com/en/2.0/ref/settings/#databases 78 | 79 | DATABASES = { 80 | "default": { 81 | "ENGINE": "django.db.backends.sqlite3", 82 | "NAME": os.path.join(BASE_DIR, "db.sqlite3"), 83 | } 84 | } 85 | 86 | 87 | # Password validation 88 | # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators 89 | 90 | # 0 too guessable: risky password. (guesses < 10^3) 91 | # 1 very guessable: protection from throttled online attacks. (guesses < 10^6) 92 | # 2 somewhat guessable: protection from unthrottled online attacks. (guesses < 10^8) 93 | # 3 safely unguessable: moderate protection from offline slow-hash scenario. (guesses < 10^10) 94 | # 4 very unguessable: strong protection from offline slow-hash scenario. (guesses >= 10^10) 95 | PASSWORD_MINIMAL_STRENGTH = 3 96 | AUTH_PASSWORD_VALIDATORS = [ 97 | { 98 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" 99 | }, 100 | {"NAME": "django_zxcvbn_password_validator.ZxcvbnPasswordValidator"}, 101 | ] 102 | 103 | 104 | # Internationalization 105 | # https://docs.djangoproject.com/en/2.0/topics/i18n/ 106 | 107 | LANGUAGE_CODE = "fr" 108 | 109 | LOCALE_PATHS = [os.path.join(BASE_DIR, "django_zxcvbn_password_validator", "locale")] 110 | 111 | TIME_ZONE = "UTC" 112 | 113 | USE_I18N = True 114 | 115 | USE_L10N = True 116 | 117 | USE_TZ = True 118 | 119 | 120 | # Static files (CSS, JavaScript, Images) 121 | # https://docs.djangoproject.com/en/2.0/howto/static-files/ 122 | 123 | STATIC_URL = "/static/" 124 | -------------------------------------------------------------------------------- /translation_project/urls.py: -------------------------------------------------------------------------------- 1 | """translation_project URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.0/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | 17 | from django.conf import settings 18 | 19 | try: 20 | from django.conf.urls import url 21 | except ImportError: 22 | # Django 4.0 replaced url by something else 23 | # See https://stackoverflow.com/a/70319607/2519059 24 | from django.urls import re_path as url 25 | 26 | from django.contrib import admin 27 | from django.urls import path 28 | from django.urls.conf import include 29 | 30 | urlpatterns = [path("admin/", admin.site.urls)] 31 | 32 | if "rosetta" in settings.INSTALLED_APPS: 33 | urlpatterns += [url(r"^rosetta/", include("rosetta.urls"))] 34 | -------------------------------------------------------------------------------- /translation_project/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for translation_project project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "translation_project.settings") 15 | 16 | application = get_wsgi_application() 17 | --------------------------------------------------------------------------------