[build-system] build-backend = "setuptools.build_meta" requires = [ "setuptools==84.1.0", "translate-toolkit==3.20.1" ] [dependency-groups] dev = [ { include-group = "docs" }, { include-group = "lint" }, { include-group = "sbom" }, { include-group = "scripts" }, { include-group = "test" }, { include-group = "types" } ] docs = [ "furo==2025.12.19", "jinja2==3.1.6", "matplotlib==3.11.2", "Pillow==13.0.0", "Pygments==2.21.0", "sphinx-copybutton==0.5.2", "sphinx-jsonschema==1.20.1", "sphinx-llm==1.1.0", "sphinx-reredirects==1.1.0", "Sphinx==8.2.3", "sphinxcontrib-httpdomain==2.2.1", "sphinxext-opengraph==0.13.0", "weblate-fonts==2026.1", "wlc==3.1.0" ] lint = [ { include-group = "pre-commit" }, { include-group = "pylint" } ] package-build = [ "setuptools==84.1.0", "translate-toolkit==3.20.1" ] package-lint = [ "check-manifest==0.51", "check-wheel-contents==0.6.3", "pydistcheck==0.11.3", "pyroma==5.0.1", "twine==7.0.0" ] pre-commit = [ "prek==0.5.7" ] pylint = [ "pylint==4.1.3" ] sbom = [ "packaging==26.3" ] schemas = [ "weblate-schemas==2026.13" ] scripts = [ "django-debug-toolbar==8.0.0", "lxml==6.1.3", "PyICU==2.17", "reuse==6.2.0", "scour==0.38.2" ] test = [ "coverage==7.17.0", "hypothesis==6.170.11", "pytest-cov==7.1.0", "pytest-django==4.14.0", "pytest-github-actions-annotate-failures==0.4.2", "pytest-profiling==1.8.1", "pytest-xdist==3.8.0", "pytest==9.1.1", "responses==0.26.3", "selenium==4.51.0", "standardwebhooks==1.1.0" ] types = [ "boto3-stubs==1.43.118", "celery-types==0.26.0", "django-stubs-ext==6.1.2", "django-stubs[compatible-mypy]==6.1.2", "djangorestframework-stubs==3.18.1", "mypy==2.3.1", "types-dateparser==1.4.3.20260907", "types-docutils==0.23.0.20260923", "types-jsonschema==4.26.0.20261006", "types-lxml==2026.2.16", "types-openpyxl==3.1.5.20261017", "types-python-dateutil==2.9.0.20261021", "types-regex==2026.9.29.20261017", "types-setuptools==84.0.0.20261006" ] [project] authors = [ { email = "michal@weblate.org", name = "Michal Čihař" } ] classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Framework :: Django", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Internationalization", "Topic :: Software Development :: Localization" ] dependencies = [ # Pin for translate-toolkit extra "aeidon==2.0.1", "ahocorasick-rs==1.0.3", "altcha==2.3.0", "argon2-cffi-bindings==26.1.0", "argon2-cffi==25.1.0", "arsc-writer==0.1.0", "borgbackup==1.4.5", # Transitional dep for httpx2 and urllib3 "brotli==1.2.1", "cel-expr-python==0.1.3", "celery[redis]==5.6.3", # We intentionally allow newer SSL certs bundle, as that should not be breaking "certifi>=2026.10.23", # For Argon2 "cffi==2.1.1", "charset-normalizer==3.5.2", "confusable-homoglyphs==3.3.1", "crispy-bootstrap5==2026.9", "cryptography==50.0.2", "cssselect==1.6.0", "cyrtranslit==1.2.0", "cysignals==1.12.6", "dateparser==1.4.3", "diff-match-patch==20241021", "disposable-email-domains==0.0.286", "django-appconf==1.2.0", "django-celery-beat==2.9.0", "django-cors-headers==4.9.0", "django-crispy-forms==2.7", "django-filter[drf]==26.2", "django-otp-webauthn==0.10.3", "django-otp==1.7.5", "django-redis==7.0.0", "Django[argon2]==6.0.9", "djangorestframework-csv==3.0.2", "djangorestframework==3.18.3", "docutils==0.21.2", "drf-spectacular[sidecar]==0.30.0", "drf-standardized-errors[openapi]==0.16.0", "fedora-messaging==3.9.1", "filelock==4.2.4", # Pin for translate-toolkit extra "fluent.syntax==0.19.0", "GitPython==3.2.1", "hiredis==3.5.0", "httpx2[brotli,socks,zstd]==2.16.0", "idna==3.20", # Pin for translate-toolkit extra "iniparse==0.5", "jsonschema==4.26.0", "lxml==6.1.3", "matplotlib==3.11.2", "mistletoe==1.6.0", # Transitional dependency for borg "msgpack==1.2.1", "nh3==0.3.7", "openpyxl==3.1.5", "opentelemetry-exporter-otlp-proto-http==1.45.1", "opentelemetry-instrumentation-celery==0.66b1", "opentelemetry-instrumentation-django==0.66b1", "opentelemetry-instrumentation-psycopg==0.66b1", "opentelemetry-instrumentation-redis==0.66b1", "opentelemetry-instrumentation-requests==0.66b1", "opentelemetry-sdk==1.45.1", "packaging==26.3", # Pin for translate-toolkit extra "phply==1.2.6", "Pillow==13.0.0", "pyaskalono==0.2.0", "pyasn1==0.6.5", "Pygments==2.21.0", "pyicumessageformat==1.0.0", "PyJWT==2.15.1", "pyOpenSSL==26.4.0", "pyparsing==3.3.3", "python-dateutil==2.9.0.post0", "qrcode==8.2", "rapidfuzz==3.14.6", "redis==6.4.0", "regex==2026.9.29", "requests==2.34.2", "ruamel.yaml==0.19.1", "sentry-sdk==2.73.0", "siphashc==2.9.1", "social-auth-app-django==7.2.0", "social-auth-core==6.2.0", "tesserocr==2.12.0", # Pin for translate-toolkit extra "tomlkit==0.15.2", "translate-toolkit[chardet,fluent,ini,markdown,toml,php,rc,subtitles,yaml]==3.20.1", "translation-finder==3.9.0", "turbohtml==1.15.4", "twisted==26.4.0", "ua-parser==1.0.2", "unicode-segmentation-rs==1.0.0", "unidecode==1.4.0", "urllib3[brotli,zstd]==2.8.0", "user-agents==2.2.0", "webauthn==3.0.1", "weblate-fonts==2026.1", "weblate-language-data==2026.11", "weblate-schemas==2026.13" ] description = "A web-based continuous localization system with tight version control integration" keywords = [ "gettext", "git", "i18n", "l10n", "mercurial", "translate" ] license = "GPL-3.0-or-later" license-files = ["LICENSE", "LICENSES/*.txt", "REUSE.toml"] name = "weblate" requires-python = ">=3.12" version = "2026.11.dev0" [project.optional-dependencies] all = [ "weblate[amazon,gelf,gerrit,google,google-errors,ldap,mercurial,postgres,rollbar,sphinx,zxcvbn]" ] amazon = [ "boto3==1.43.118", "botocore==1.43.118", "django-ses==4.8.2" ] asgi = [ "granian==2.8.5" ] gelf = [ "logging-gelf==0.0.32" ] gerrit = [ "git-review==2.5.0" ] google = [ "google-cloud-storage==3.17.1", "google-cloud-translate==3.28.0" ] google-errors = [ "google-cloud-error-reporting==1.17.0" ] ldap = [ "django-auth-ldap==5.4.0" ] mercurial = [ "mercurial==7.2.4" ] oidc = [ "django-oauth-toolkit==3.4.1" ] postgres = [ "psycopg[binary]==3.3.6" ] rollbar = [ "rollbar==1.5.0" ] saml = [ "python3-saml==1.16.0", "xmlsec==1.3.17" ] sphinx = [ "Sphinx==8.2.3" ] wllegal = [ "wllegal==2026.6" ] wsgi = [ "granian==2.8.5" ] zxcvbn = [ "django-zxcvbn-password-validator==1.7.0" ] [project.readme] content-type = "text/x-rst" file = "README.rst" [project.scripts] weblate = "weblate.runner:main" weblate-generate-secret-key = "weblate.utils.generate_secret_key:main" [project.urls] Documentation = "https://docs.weblate.org/" Download = "https://weblate.org/download/" Funding = "https://weblate.org/donate/" Homepage = "https://weblate.org/" "Issue Tracker" = "https://github.com/WeblateOrg/weblate/issues" "Release Notes" = "https://docs.weblate.org/en/latest/changes.html" "Source Code" = "https://github.com/WeblateOrg/weblate" X = "https://x.com/WeblateOrg" [tool.check-manifest] ignore = [ "*.json", "*.toml", "*.yaml", "*.yml", ".dclintrc", ".devcontainer/*", ".dockerignore", ".editorconfig", ".gitmodules", ".imgbotconfig", ".oss-scanner/*", ".reuse/dep5", ".weblate", ".well-known/*", ".yamllint.yml", "AGENTS.md", "ci/*", "ci/migrate-scripts/*", "client/*", "client/*/*", "dev-docker/*", "dev-docker/*/*", "docs/*", "docs/*/*", "docs/*/*/*", "docs/*/*/*/*", "LICENSES/*", "rundev.sh", "scripts/*", "scripts/*/*", "scripts/*/*/*", "scripts/*/*/*/*" ] ignore-bad-ideas = [ "weblate/trans/tests/data/cs.mo" # Test data ] [tool.check-wheel-contents] ignore = [ "W002", # Triggered by __init__.py or .license files "W004" # Django migrations fail here ] package = [ "weblate" ] package_omit = [ "*.egg-info", "*.pyc", "*.pyo", ".*", "CVS", "RCS", "weblate/*/test-data/*", "weblate/*/tests.py", "weblate/*/tests/*", "weblate/examples/apache-path.conf", "weblate/examples/apache.conf", "weblate/examples/apache.gunicorn.conf", "weblate/examples/benchmark", "weblate/examples/celery", "weblate/examples/celery-weblate.conf", "weblate/examples/celery-weblate.logrotate", "weblate/examples/celery-weblate.service", "weblate/examples/docker-compose.yml", "weblate/examples/granian-asgi.service", "weblate/examples/granian.service", "weblate/examples/gunicorn.service", "weblate/examples/gunicorn.socket", "weblate/examples/weblate.nginx.cdn.conf", "weblate/examples/weblate.nginx.conf", "weblate/examples/weblate.nginx.granian.conf", "weblate/examples/weblate.nginx.gunicorn.conf", "weblate/examples/weblate.uwsgi.ini", "weblate/locale/*.pot", "weblate/locale/*/LC_MESSAGES/*.po", "weblate/settings.py", "weblate/static/js/vendor/oss-licenses.json", "weblate/trans/fixtures/*" ] [tool.codespell] builtin = "clear,rare,informal,usage" count = true dictionary = "scripts/codespell.txt,-" # Vai is a language name ignore-words-list = "assertIn,Vai,master" skip = '*.po,*.pot,*.json,*.tmx,*.tbx,*.csv,*/vendor/*,weblate/trans/specialchars.py,weblate/trans/tests/data/known_hosts,docs/changes/contributors/*.rst,LICENSES/*,REUSE.toml' [tool.coverage.paths] source = [ "." ] [tool.coverage.report] exclude_also = [ "@(abc\\.)?abstractmethod", # Don't complain about missing debug-only code: "def __repr__", # Don't complain if non-runnable code isn't run: "if 0:", "if __name__ == .__main__.:", "if self\\.debug", # Type checking "if TYPE_CHECKING:", # Have to re-enable the standard pragma "pragma: no cover", # Don't complain if tests don't hit defensive assertion code: "raise AssertionError", "raise NotImplementedError" ] [tool.coverage.run] branch = true concurrency = ["multiprocessing", "thread"] omit = [ ".venv*/*", "weblate/settings.py", "weblate/settings_*.py" ] [tool.django-stubs] django_settings_module = "weblate.settings_test" strict_settings = false [tool.djlint] blank_line_after_tag = "load,extends,endblock" # This is actually tmx formatting: custom_html = "tmx,header,body,tuv,tu,seg" # - reconsider H014 once formatter is in use # T003 - Endblock should have name. # T002 - Double quotes should be used in tags. (makes using tags in attributes hard) # H013 - Img tag should have an alt attribute. # H006 - Img tag should have height and width attributes. # H021 - Inline styles should be avoided. # H031 - Consider adding meta keywords. ignore = "T003,H014,T002,H013,H006,H021,H031" indent = 2 max_attribute_length = 80 max_blank_lines = 2 preserve_blank_lines = true profile = "django" [tool.djlint.per-file-ignores] # Mail templates need inline CSS and do not utilize description "weblate/templates/mail/base.html" = "H030" # Test file "weblate/trans/tests/data/cs.html" = "H005,H007,H016,H030" [tool.mypy] check_untyped_defs = true plugins = [ "mypy_django_plugin.main", "mypy_drf_plugin.main" ] [[tool.mypy.overrides]] disallow_untyped_defs = true ignore_missing_imports = true module = [ "appconf.*", "askalono.*", "borg.*", "confusable_homoglyphs.*", "crispy_forms.*", "cyrtranslit.*", "debug_toolbar.*", "defusedxml.*", "diff_match_patch.*", "django.db.backends.postgresql.psycopg_any", "django_auth_ldap.*", "django_celery_beat.*", "django_filters.*", "django_otp.*", "django_otp_webauthn.*", "django_redis.*", "google.oauth2.*", "icu.*", "ldap.*", "mistletoe.*", "oauth2_provider.*", "oauthlib.*", "pygments.*", "pyicumessageformat.*", "qrcode.*", "rest_framework_csv.*", "rollbar.*", "ruamel.*", "simple_sso.*", "social_django.*", "tesserocr.*", "user_agents.*", "wlhosted.*", "wllegal.*" ] [tool.pydistcheck] max_allowed_files = 2500 max_allowed_size_compressed = '100M' max_allowed_size_uncompressed = '300M' [tool.pylint.main] disable = [ "abstract-method", # TODO "attribute-defined-outside-init", # TODO "broad-exception-caught", # TODO "c-extension-no-member", "cyclic-import", "duplicate-code", # TODO "fixme", "import-error", "import-outside-toplevel", "invalid-name", # TODO "invalid-overridden-method", # TODO "invalid-str-returned", # Most of Django __str__ fails this "keyword-arg-before-vararg", # TODO "line-too-long", "missing-class-docstring", "missing-function-docstring", "missing-module-docstring", "no-member", "no-name-in-module", "not-a-mapping", "not-an-iterable", "not-callable", "protected-access", # TODO "raising-bad-type", "too-few-public-methods", "too-many-ancestors", "too-many-arguments", "too-many-boolean-expressions", "too-many-branches", "too-many-instance-attributes", "too-many-lines", "too-many-locals", "too-many-nested-blocks", "too-many-positional-arguments", "too-many-public-methods", "too-many-return-statements", "too-many-statements", "unsubscriptable-object", "unsupported-assignment-operation", # TODO "unsupported-binary-operation", "unsupported-delete-operation", # TODO "unsupported-membership-test", "unused-argument" ] extension-pkg-whitelist = ["siphashc"] ignore = [ ".git", ".venv", "build", "migrations", "repos", "settings.py", "settings_test.py", "test-repos" ] [tool.pytest.ini_options] addopts = "--reuse-db --cov=weblate --cov-report= --durations=20 --durations-min=2 --ignore=data --ignore=data-test --ignore=dev-docker --ignore=scripts" DJANGO_SETTINGS_MODULE = "weblate.settings_test" FAIL_INVALID_TEMPLATE_VARS = true # The single quote is used intentionall to be sorted by toml-sort at the end # what is needed to make them match as the last match is used. filterwarnings = [ # Treat all unfiltered warnings as errors "error", # Warning in the third-party module (drf_spectacular) "ignore:'_UnionGenericAlias':DeprecationWarning:drf_spectacular.plumbing", # Warning in the third-party module (python3-saml) "ignore:datetime.datetime.utcnow:DeprecationWarning:onelogin.saml2.utils", # Warning in the third-party module (openid) "ignore:defusedxml.cElementTree is deprecated, import from defusedxml.ElementTree instead.:DeprecationWarning:openid.oidutil", # There is no better way to differentiate DATA_DIR for the xdist executed tests "ignore:Implicitly cleaning up